LibreOffice Module sw (master) 1
AccessibilityCheck.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 */
10
11#include <AccessibilityCheck.hxx>
12#include <AccessibilityIssue.hxx>
13#include <AccessibilityCheckStrings.hrc>
14#include <strings.hrc>
15#include <ndnotxt.hxx>
16#include <ndtxt.hxx>
17#include <docsh.hxx>
19#include <drawdoc.hxx>
20#include <svx/svdpage.hxx>
21#include <sortedobjs.hxx>
22#include <swtable.hxx>
23#include <com/sun/star/frame/XModel.hpp>
24#include <com/sun/star/text/XTextContent.hpp>
25#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
26#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
27#include <unoparagraph.hxx>
29#include <tools/urlobj.hxx>
30#include <editeng/langitem.hxx>
31#include <calbck.hxx>
32#include <charatr.hxx>
33#include <svx/xfillit0.hxx>
34#include <svx/xflclit.hxx>
35#include <ftnidx.hxx>
36#include <txtftn.hxx>
37#include <txtfrm.hxx>
38#include <svl/itemiter.hxx>
39#include <o3tl/vector_utils.hxx>
40#include <svx/swframetypes.hxx>
41#include <fmtanchr.hxx>
42#include <dcontact.hxx>
43#include <svx/svdoashp.hxx>
44#include <svx/sdasitm.hxx>
45
46namespace sw
47{
48namespace
49{
50SwTextNode* lclSearchNextTextNode(SwNode* pCurrent)
51{
52 SwTextNode* pTextNode = nullptr;
53
54 auto nIndex = pCurrent->GetIndex();
55 auto nCount = pCurrent->GetNodes().Count();
56
57 nIndex++; // go to next node
58
59 while (pTextNode == nullptr && nIndex < nCount)
60 {
61 auto pNode = pCurrent->GetNodes()[nIndex];
62 if (pNode->IsTextNode())
63 pTextNode = pNode->GetTextNode();
64 nIndex++;
65 }
66
67 return pTextNode;
68}
69
70std::shared_ptr<sw::AccessibilityIssue>
71lclAddIssue(sfx::AccessibilityIssueCollection& rIssueCollection, OUString const& rText,
73{
74 auto pIssue = std::make_shared<sw::AccessibilityIssue>(eIssue);
75 pIssue->m_aIssueText = rText;
76 rIssueCollection.getIssues().push_back(pIssue);
77 return pIssue;
78}
79
80class NodeCheck : public BaseCheck
81{
82public:
83 NodeCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
84 : BaseCheck(rIssueCollection)
85 {
86 }
87
88 virtual void check(SwNode* pCurrent) = 0;
89};
90
91// Check NoTextNodes: Graphic, OLE for alt (title) text
92class NoTextNodeAltTextCheck : public NodeCheck
93{
94 void checkNoTextNode(SwNoTextNode* pNoTextNode)
95 {
96 if (!pNoTextNode)
97 return;
98
99 OUString sAlternative = pNoTextNode->GetTitle();
100 if (!sAlternative.isEmpty())
101 return;
102
103 OUString sName = pNoTextNode->GetFlyFormat()->GetName();
104
105 OUString sIssueText = SwResId(STR_NO_ALT).replaceAll("%OBJECT_NAME%", sName);
106
107 if (pNoTextNode->IsOLENode())
108 {
109 auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
111 pIssue->setDoc(pNoTextNode->GetDoc());
112 pIssue->setIssueObject(IssueObject::OLE);
113 pIssue->setObjectID(pNoTextNode->GetFlyFormat()->GetName());
114 }
115 else if (pNoTextNode->IsGrfNode())
116 {
117 auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
119 pIssue->setDoc(pNoTextNode->GetDoc());
120 pIssue->setIssueObject(IssueObject::GRAPHIC);
121 pIssue->setObjectID(pNoTextNode->GetFlyFormat()->GetName());
122 }
123 }
124
125public:
126 NoTextNodeAltTextCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
127 : NodeCheck(rIssueCollection)
128 {
129 }
130
131 void check(SwNode* pCurrent) override
132 {
133 if (pCurrent->GetNodeType() & SwNodeType::NoTextMask)
134 {
135 SwNoTextNode* pNoTextNode = pCurrent->GetNoTextNode();
136 if (pNoTextNode)
137 checkNoTextNode(pNoTextNode);
138 }
139 }
140};
141
142// Check Table node if the table is merged and split.
143class TableNodeMergeSplitCheck : public NodeCheck
144{
145private:
146 void addTableIssue(SwTable const& rTable, SwDoc& rDoc)
147 {
148 const SwTableFormat* pFormat = rTable.GetFrameFormat();
149 OUString sName = pFormat->GetName();
150 OUString sIssueText = SwResId(STR_TABLE_MERGE_SPLIT).replaceAll("%OBJECT_NAME%", sName);
151 auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
153 pIssue->setDoc(rDoc);
154 pIssue->setIssueObject(IssueObject::TABLE);
155 pIssue->setObjectID(sName);
156 }
157
158 void checkTableNode(SwTableNode* pTableNode)
159 {
160 if (!pTableNode)
161 return;
162
163 SwTable const& rTable = pTableNode->GetTable();
164 SwDoc& rDoc = pTableNode->GetDoc();
165 if (rTable.IsTableComplex())
166 {
167 addTableIssue(rTable, rDoc);
168 }
169 else
170 {
171 if (rTable.GetTabLines().size() > 1)
172 {
173 int i = 0;
174 size_t nFirstLineSize = 0;
175 bool bAllColumnsSameSize = true;
176 bool bCellSpansOverMoreRows = false;
177
178 for (SwTableLine const* pTableLine : rTable.GetTabLines())
179 {
180 if (i == 0)
181 {
182 nFirstLineSize = pTableLine->GetTabBoxes().size();
183 }
184 else
185 {
186 size_t nLineSize = pTableLine->GetTabBoxes().size();
187 if (nFirstLineSize != nLineSize)
188 {
189 bAllColumnsSameSize = false;
190 }
191 }
192 i++;
193
194 // Check for row span in each table box (cell)
195 for (SwTableBox const* pBox : pTableLine->GetTabBoxes())
196 {
197 if (pBox->getRowSpan() > 1)
198 bCellSpansOverMoreRows = true;
199 }
200 }
201 if (!bAllColumnsSameSize || bCellSpansOverMoreRows)
202 {
203 addTableIssue(rTable, rDoc);
204 }
205 }
206 }
207 }
208
209public:
210 TableNodeMergeSplitCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
211 : NodeCheck(rIssueCollection)
212 {
213 }
214
215 void check(SwNode* pCurrent) override
216 {
217 if (pCurrent->GetNodeType() & SwNodeType::Table)
218 {
219 SwTableNode* pTableNode = pCurrent->GetTableNode();
220 if (pTableNode)
221 checkTableNode(pTableNode);
222 }
223 }
224};
225
226class TableFormattingCheck : public NodeCheck
227{
228private:
229 void checkTableNode(SwTableNode* pTableNode)
230 {
231 if (!pTableNode)
232 return;
233
234 const SwTable& rTable = pTableNode->GetTable();
235 if (!rTable.IsTableComplex())
236 {
237 size_t nEmptyBoxes = 0;
238 size_t nBoxCount = 0;
239 for (const SwTableLine* pTableLine : rTable.GetTabLines())
240 {
241 nBoxCount += pTableLine->GetTabBoxes().size();
242 for (const SwTableBox* pBox : pTableLine->GetTabBoxes())
243 if (pBox->IsEmpty())
244 ++nEmptyBoxes;
245 }
246 // If more than half of the boxes are empty we can assume that it is used for formatting
247 if (nEmptyBoxes > nBoxCount / 2)
248 lclAddIssue(m_rIssueCollection, SwResId(STR_TABLE_FORMATTING),
250 }
251 }
252
253public:
254 TableFormattingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
255 : NodeCheck(rIssueCollection)
256 {
257 }
258
259 void check(SwNode* pCurrent) override
260 {
261 if (pCurrent->GetNodeType() & SwNodeType::Table)
262 {
263 SwTableNode* pTableNode = pCurrent->GetTableNode();
264 if (pTableNode)
265 checkTableNode(pTableNode);
266 }
267 }
268};
269
270class NumberingCheck : public NodeCheck
271{
272private:
273 const std::vector<std::pair<OUString, OUString>> m_aNumberingCombinations{
274 { "1.", "2." }, { "(1)", "(2)" }, { "1)", "2)" }, { "a.", "b." }, { "(a)", "(b)" },
275 { "a)", "b)" }, { "A.", "B." }, { "(A)", "(B)" }, { "A)", "B)" }
276 };
277
278public:
279 NumberingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
280 : NodeCheck(rIssueCollection)
281 {
282 }
283
284 void check(SwNode* pCurrent) override
285 {
286 if (!pCurrent->IsTextNode())
287 return;
288
289 SwTextNode* pCurrentTextNode = pCurrent->GetTextNode();
290 SwTextNode* pNextTextNode = lclSearchNextTextNode(pCurrent);
291
292 if (!pNextTextNode)
293 return;
294
295 for (auto& rPair : m_aNumberingCombinations)
296 {
297 if (pCurrentTextNode->GetText().startsWith(rPair.first)
298 && pNextTextNode->GetText().startsWith(rPair.second))
299 {
300 OUString sNumbering = rPair.first + " " + rPair.second + "...";
301 OUString sIssueText
302 = SwResId(STR_FAKE_NUMBERING).replaceAll("%NUMBERING%", sNumbering);
303 lclAddIssue(m_rIssueCollection, sIssueText,
305 }
306 }
307 }
308};
309
310class HyperlinkCheck : public NodeCheck
311{
312private:
313 void checkTextRange(uno::Reference<text::XTextRange> const& xTextRange, SwTextNode* pTextNode,
314 sal_Int32 nStart)
315 {
316 uno::Reference<beans::XPropertySet> xProperties(xTextRange, uno::UNO_QUERY);
317 if (!xProperties->getPropertySetInfo()->hasPropertyByName("HyperLinkURL"))
318 return;
319
320 OUString sHyperlink;
321 xProperties->getPropertyValue("HyperLinkURL") >>= sHyperlink;
322 if (!sHyperlink.isEmpty())
323 {
324 OUString sText = xTextRange->getString();
325 if (INetURLObject(sText) == INetURLObject(sHyperlink))
326 {
327 OUString sIssueText
328 = SwResId(STR_HYPERLINK_TEXT_IS_LINK).replaceFirst("%LINK%", sHyperlink);
329 lclAddIssue(m_rIssueCollection, sIssueText,
331 }
332 else if (sText.getLength() <= 5)
333 {
334 auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_HYPERLINK_TEXT_IS_SHORT),
336 pIssue->setIssueObject(IssueObject::TEXT);
337 pIssue->setNode(pTextNode);
338 SwDoc& rDocument = pTextNode->GetDoc();
339 pIssue->setDoc(rDocument);
340 pIssue->setStart(nStart);
341 pIssue->setEnd(nStart + sText.getLength());
342 }
343 }
344 }
345
346public:
347 HyperlinkCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
348 : NodeCheck(rIssueCollection)
349 {
350 }
351
352 void check(SwNode* pCurrent) override
353 {
354 if (!pCurrent->IsTextNode())
355 return;
356
357 SwTextNode* pTextNode = pCurrent->GetTextNode();
358 uno::Reference<text::XTextContent> xParagraph
359 = SwXParagraph::CreateXParagraph(pTextNode->GetDoc(), pTextNode);
360 if (!xParagraph.is())
361 return;
362
363 uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph, uno::UNO_QUERY);
364 uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
365 sal_Int32 nStart = 0;
366 while (xRunEnum->hasMoreElements())
367 {
368 uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
369 if (xRun.is())
370 {
371 checkTextRange(xRun, pTextNode, nStart);
372 nStart += xRun->getString().getLength();
373 }
374 }
375 }
376};
377
378// Based on https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
379double calculateRelativeLuminance(Color const& rColor)
380{
381 // Convert to BColor which has R, G, B colors components
382 // represented by a floating point number from [0.0, 1.0]
383 const basegfx::BColor aBColor = rColor.getBColor();
384
385 double r = aBColor.getRed();
386 double g = aBColor.getGreen();
387 double b = aBColor.getBlue();
388
389 // Calculate the values according to the described algorithm
390 r = (r <= 0.03928) ? r / 12.92 : std::pow((r + 0.055) / 1.055, 2.4);
391 g = (g <= 0.03928) ? g / 12.92 : std::pow((g + 0.055) / 1.055, 2.4);
392 b = (b <= 0.03928) ? b / 12.92 : std::pow((b + 0.055) / 1.055, 2.4);
393
394 return 0.2126 * r + 0.7152 * g + 0.0722 * b;
395}
396
397// TODO move to common color tools (BColorTools maybe)
398// Based on https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio
399double calculateContrastRatio(Color const& rColor1, Color const& rColor2)
400{
401 const double fLuminance1 = calculateRelativeLuminance(rColor1);
402 const double fLuminance2 = calculateRelativeLuminance(rColor2);
403 const std::pair<const double, const double> aMinMax = std::minmax(fLuminance1, fLuminance2);
404
405 // (L1 + 0.05) / (L2 + 0.05)
406 // L1 is the lighter color (greater luminance value)
407 // L2 is the darker color (smaller luminance value)
408 return (aMinMax.second + 0.05) / (aMinMax.first + 0.05);
409}
410
411class TextContrastCheck : public NodeCheck
412{
413private:
414 void checkTextRange(uno::Reference<text::XTextRange> const& xTextRange,
415 uno::Reference<text::XTextContent> const& xParagraph, SwTextNode* pTextNode,
416 sal_Int32 nTextStart)
417 {
418 Color nParaBackColor(COL_AUTO);
419 uno::Reference<beans::XPropertySet> xParagraphProperties(xParagraph, uno::UNO_QUERY);
420 if (!(xParagraphProperties->getPropertyValue("ParaBackColor") >>= nParaBackColor))
421 {
422 SAL_WARN("sw.a11y", "ParaBackColor void");
423 return;
424 }
425
426 uno::Reference<beans::XPropertySet> xProperties(xTextRange, uno::UNO_QUERY);
427 if (!xProperties.is())
428 return;
429
430 // Foreground color
431 sal_Int32 nCharColor = {}; // spurious -Werror=maybe-uninitialized
432 if (!(xProperties->getPropertyValue("CharColor") >>= nCharColor))
433 { // not sure this is impossible, can the default be void?
434 SAL_WARN("sw.a11y", "CharColor void");
435 return;
436 }
437
438 const SwPageDesc* pPageDescription = pTextNode->FindPageDesc();
439 const SwFrameFormat& rPageFormat = pPageDescription->GetMaster();
440 const SwAttrSet& rPageSet = rPageFormat.GetAttrSet();
441
442 const XFillStyleItem* pXFillStyleItem(
443 rPageSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE, false));
444 Color aPageBackground(COL_AUTO);
445
446 if (pXFillStyleItem && pXFillStyleItem->GetValue() == css::drawing::FillStyle_SOLID)
447 {
448 const XFillColorItem* rXFillColorItem
449 = rPageSet.GetItem<XFillColorItem>(XATTR_FILLCOLOR, false);
450 aPageBackground = rXFillColorItem->GetColorValue();
451 }
452
453 Color nCharBackColor(COL_AUTO);
454
455 if (!(xProperties->getPropertyValue("CharBackColor") >>= nCharBackColor))
456 {
457 SAL_WARN("sw.a11y", "CharBackColor void");
458 return;
459 }
460 // Determine the background color
461 // Try Character background (highlight)
462 Color aBackgroundColor(nCharBackColor);
463
464 // If not character background color, try paragraph background color
465 if (aBackgroundColor == COL_AUTO)
466 aBackgroundColor = nParaBackColor;
467 else
468 {
469 auto pIssue
470 = lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_FORMATTING_CONVEYS_MEANING),
472 pIssue->setIssueObject(IssueObject::TEXT);
473 pIssue->setNode(pTextNode);
474 SwDoc& rDocument = pTextNode->GetDoc();
475 pIssue->setDoc(rDocument);
476 pIssue->setStart(nTextStart);
477 pIssue->setEnd(nTextStart + xTextRange->getString().getLength());
478 }
479
480 Color aForegroundColor(ColorTransparency, nCharColor);
481 if (aForegroundColor == COL_AUTO)
482 return;
483
484 // If not paragraph background color, try page color
485 if (aBackgroundColor == COL_AUTO)
486 aBackgroundColor = aPageBackground;
487
488 // If not page color, assume white background color
489 if (aBackgroundColor == COL_AUTO)
490 aBackgroundColor = COL_WHITE;
491
492 double fContrastRatio = calculateContrastRatio(aForegroundColor, aBackgroundColor);
493 if (fContrastRatio < 4.5)
494 {
495 lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_CONTRAST));
496 }
497 }
498
499public:
500 TextContrastCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
501 : NodeCheck(rIssueCollection)
502 {
503 }
504
505 void check(SwNode* pCurrent) override
506 {
507 if (!pCurrent->IsTextNode())
508 return;
509
510 SwTextNode* pTextNode = pCurrent->GetTextNode();
511 uno::Reference<text::XTextContent> xParagraph;
512 xParagraph = SwXParagraph::CreateXParagraph(pTextNode->GetDoc(), pTextNode);
513 if (!xParagraph.is())
514 return;
515
516 uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph, uno::UNO_QUERY);
517 uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
518 sal_Int32 nStart = 0;
519 while (xRunEnum->hasMoreElements())
520 {
521 uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
522 if (xRun.is())
523 {
524 checkTextRange(xRun, xParagraph, pTextNode, nStart);
525 nStart += xRun->getString().getLength();
526 }
527 }
528 }
529};
530
531class TextFormattingCheck : public NodeCheck
532{
533public:
534 TextFormattingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
535 : NodeCheck(rIssueCollection)
536 {
537 }
538
539 void checkAutoFormat(SwTextNode* pTextNode, const SwTextAttr* pTextAttr)
540 {
541 const SwFormatAutoFormat& rAutoFormat = pTextAttr->GetAutoFormat();
542 SfxItemIter aItemIter(*rAutoFormat.GetStyleHandle());
543 const SfxPoolItem* pItem = aItemIter.GetCurItem();
544 std::vector<OUString> aFormattings;
545 while (pItem)
546 {
547 OUString sFormattingType;
548 switch (pItem->Which())
549 {
553 sFormattingType = "Weight";
554 break;
558 sFormattingType = "Posture";
559 break;
560
562 sFormattingType = "Shadowed";
563 break;
564
565 case RES_CHRATR_COLOR:
566 sFormattingType = "Font Color";
567 break;
568
572 sFormattingType = "Font Size";
573 break;
574
575 case RES_CHRATR_FONT:
578 sFormattingType = "Font";
579 break;
580
582 sFormattingType = "Emphasis Mark";
583 break;
584
586 sFormattingType = "Underline";
587 break;
588
590 sFormattingType = "Overline";
591 break;
592
594 sFormattingType = "Strikethrough";
595 break;
596
598 sFormattingType = "Relief";
599 break;
600
602 sFormattingType = "Outline";
603 break;
604 default:
605 break;
606 }
607 if (!sFormattingType.isEmpty())
608 aFormattings.push_back(sFormattingType);
609 pItem = aItemIter.NextItem();
610 }
611 if (aFormattings.empty())
612 return;
613
614 o3tl::remove_duplicates(aFormattings);
615 auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_FORMATTING_CONVEYS_MEANING),
617 pIssue->setIssueObject(IssueObject::TEXT);
618 pIssue->setNode(pTextNode);
619 SwDoc& rDocument = pTextNode->GetDoc();
620 pIssue->setDoc(rDocument);
621 pIssue->setStart(pTextAttr->GetStart());
622 pIssue->setEnd(pTextAttr->GetAnyEnd());
623 }
624
625 void check(SwNode* pCurrent) override
626 {
627 if (!pCurrent->IsTextNode())
628 return;
629
630 SwTextNode* pTextNode = pCurrent->GetTextNode();
631 if (pTextNode->HasHints())
632 {
633 SwpHints& rHints = pTextNode->GetSwpHints();
634 for (size_t i = 0; i < rHints.Count(); ++i)
635 {
636 const SwTextAttr* pTextAttr = rHints.Get(i);
637 if (pTextAttr->Which() == RES_TXTATR_AUTOFMT)
638 {
639 checkAutoFormat(pTextNode, pTextAttr);
640 }
641 }
642 }
643 else if (pTextNode->HasSwAttrSet())
644 {
645 // Paragraph doesn't have hints but the entire paragraph might have char attributes
646 auto& aSwAttrSet = pTextNode->GetSwAttrSet();
647 auto nParagraphLength = pTextNode->GetText().getLength();
648 if (nParagraphLength == 0)
649 return;
650 if (aSwAttrSet.HasItem(RES_CHRATR_WEIGHT) || aSwAttrSet.HasItem(RES_CHRATR_CJK_WEIGHT)
651 || aSwAttrSet.HasItem(RES_CHRATR_CTL_WEIGHT)
652 || aSwAttrSet.HasItem(RES_CHRATR_POSTURE)
653 || aSwAttrSet.HasItem(RES_CHRATR_CJK_POSTURE)
654 || aSwAttrSet.HasItem(RES_CHRATR_CTL_POSTURE)
655 || aSwAttrSet.HasItem(RES_CHRATR_SHADOWED) || aSwAttrSet.HasItem(RES_CHRATR_COLOR)
656 || aSwAttrSet.HasItem(RES_CHRATR_EMPHASIS_MARK)
657 || aSwAttrSet.HasItem(RES_CHRATR_UNDERLINE)
658 || aSwAttrSet.HasItem(RES_CHRATR_OVERLINE)
659 || aSwAttrSet.HasItem(RES_CHRATR_CROSSEDOUT)
660 || aSwAttrSet.HasItem(RES_CHRATR_RELIEF) || aSwAttrSet.HasItem(RES_CHRATR_CONTOUR))
661 {
662 auto pIssue
663 = lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_FORMATTING_CONVEYS_MEANING),
665 pIssue->setIssueObject(IssueObject::TEXT);
666 pIssue->setNode(pTextNode);
667 SwDoc& rDocument = pTextNode->GetDoc();
668 pIssue->setDoc(rDocument);
669 pIssue->setEnd(nParagraphLength);
670 }
671 }
672 }
673};
674
675class NewlineSpacingCheck : public NodeCheck
676{
677private:
678 static SwTextNode* getNextTextNode(SwNode* pCurrent)
679 {
680 SwTextNode* pTextNode = nullptr;
681
682 auto nIndex = pCurrent->GetIndex();
683 auto nCount = pCurrent->GetNodes().Count();
684
685 nIndex++; // go to next node
686
687 while (pTextNode == nullptr && nIndex < nCount)
688 {
689 auto pNode = pCurrent->GetNodes()[nIndex];
690 if (pNode->IsTextNode())
691 pTextNode = pNode->GetTextNode();
692 nIndex++;
693 }
694
695 return pTextNode;
696 }
697
698public:
699 NewlineSpacingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
700 : NodeCheck(rIssueCollection)
701 {
702 }
703 void check(SwNode* pCurrent) override
704 {
705 if (!pCurrent->IsTextNode())
706 return;
707
708 // Don't count empty table box text nodes
709 if (pCurrent->GetTableBox())
710 return;
711
712 SwTextNode* pTextNode = pCurrent->GetTextNode();
713 auto nParagraphLength = pTextNode->GetText().getLength();
714 if (nParagraphLength == 0)
715 {
716 SwTextNode* pNextTextNode = getNextTextNode(pCurrent);
717 if (!pNextTextNode)
718 return;
719 if (pNextTextNode->GetText().getLength() == 0)
720 {
721 auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_NEWLINES_SPACE),
723 pIssue->setIssueObject(IssueObject::TEXT);
724 pIssue->setNode(pNextTextNode);
725 SwDoc& rDocument = pNextTextNode->GetDoc();
726 pIssue->setDoc(rDocument);
727 }
728 }
729 else
730 {
731 // Check for excess lines inside this paragraph
732 const OUString& sParagraphText = pTextNode->GetText();
733 int nLineCount = 0;
734 for (sal_Int32 i = 0; i < nParagraphLength; i++)
735 {
736 auto aChar = sParagraphText[i];
737 if (aChar == '\n')
738 {
739 nLineCount++;
740 // Looking for 2 newline characters and above as one can be part of the line
741 // break after a sentence
742 if (nLineCount > 2)
743 {
744 auto pIssue
745 = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_NEWLINES_SPACE),
747 pIssue->setIssueObject(IssueObject::TEXT);
748 pIssue->setNode(pTextNode);
749 SwDoc& rDocument = pTextNode->GetDoc();
750 pIssue->setDoc(rDocument);
751 pIssue->setStart(i);
752 pIssue->setEnd(i);
753 }
754 }
755 // Don't count carriage return as normal character
756 else if (aChar != '\r')
757 {
758 nLineCount = 0;
759 }
760 }
761 }
762 }
763};
764
765class SpaceSpacingCheck : public NodeCheck
766{
767public:
768 SpaceSpacingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
769 : NodeCheck(rIssueCollection)
770 {
771 }
772 void check(SwNode* pCurrent) override
773 {
774 if (!pCurrent->IsTextNode())
775 return;
776 SwTextNode* pTextNode = pCurrent->GetTextNode();
777 auto nParagraphLength = pTextNode->GetText().getLength();
778 const OUString& sParagraphText = pTextNode->GetText();
779 sal_Int32 nSpaceCount = 0;
780 sal_Int32 nSpaceStart = 0;
781 sal_Int32 nTabCount = 0;
782 bool bNonSpaceFound = false;
783 bool bPreviousWasChar = false;
784 for (sal_Int32 i = 0; i < nParagraphLength; i++)
785 {
786 switch (sParagraphText[i])
787 {
788 case ' ':
789 {
790 if (bNonSpaceFound)
791 {
792 nSpaceCount++;
793 if (nSpaceCount == 2)
794 nSpaceStart = i;
795 }
796 break;
797 }
798 case '\t':
799 {
800 if (bPreviousWasChar)
801 {
802 ++nTabCount;
803 bPreviousWasChar = false;
804 if (nTabCount == 2)
805 {
806 auto pIssue = lclAddIssue(m_rIssueCollection,
807 SwResId(STR_AVOID_TABS_FORMATTING),
809 pIssue->setIssueObject(IssueObject::TEXT);
810 pIssue->setNode(pTextNode);
811 SwDoc& rDocument = pTextNode->GetDoc();
812 pIssue->setDoc(rDocument);
813 pIssue->setStart(0);
814 pIssue->setEnd(nParagraphLength);
815 }
816 }
817 break;
818 }
819 default:
820 {
821 if (nSpaceCount >= 2)
822 {
823 auto pIssue
824 = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_SPACES_SPACE),
826 pIssue->setIssueObject(IssueObject::TEXT);
827 pIssue->setNode(pTextNode);
828 SwDoc& rDocument = pTextNode->GetDoc();
829 pIssue->setDoc(rDocument);
830 pIssue->setStart(nSpaceStart);
831 pIssue->setEnd(nSpaceStart + nSpaceCount - 1);
832 }
833 bNonSpaceFound = true;
834 bPreviousWasChar = true;
835 nSpaceCount = 0;
836 break;
837 }
838 }
839 }
840 }
841};
842
843class FakeFootnoteCheck : public NodeCheck
844{
845private:
846 void checkAutoFormat(SwTextNode* pTextNode, const SwTextAttr* pTextAttr)
847 {
848 const SwFormatAutoFormat& rAutoFormat = pTextAttr->GetAutoFormat();
849 SfxItemIter aItemIter(*rAutoFormat.GetStyleHandle());
850 const SfxPoolItem* pItem = aItemIter.GetCurItem();
851 while (pItem)
852 {
853 if (pItem->Which() == RES_CHRATR_ESCAPEMENT)
854 {
855 auto pEscapementItem = static_cast<const SvxEscapementItem*>(pItem);
856 if (pEscapementItem->GetEscapement() == SvxEscapement::Superscript
857 && pTextAttr->GetStart() == 0 && pTextAttr->GetAnyEnd() == 1)
858 {
859 auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_FAKE_FOOTNOTES),
861 pIssue->setIssueObject(IssueObject::TEXT);
862 pIssue->setNode(pTextNode);
863 SwDoc& rDocument = pTextNode->GetDoc();
864 pIssue->setDoc(rDocument);
865 pIssue->setStart(0);
866 pIssue->setEnd(pTextNode->GetText().getLength());
867 break;
868 }
869 }
870 pItem = aItemIter.NextItem();
871 }
872 }
873
874public:
875 FakeFootnoteCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
876 : NodeCheck(rIssueCollection)
877 {
878 }
879 void check(SwNode* pCurrent) override
880 {
881 if (!pCurrent->IsTextNode())
882 return;
883 SwTextNode* pTextNode = pCurrent->GetTextNode();
884 if (pTextNode->GetText().getLength() == 0)
885 return;
886
887 if (pTextNode->GetText()[0] == '*')
888 {
889 auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_FAKE_FOOTNOTES),
891 pIssue->setIssueObject(IssueObject::TEXT);
892 pIssue->setNode(pTextNode);
893 SwDoc& rDocument = pTextNode->GetDoc();
894 pIssue->setDoc(rDocument);
895 pIssue->setStart(0);
896 pIssue->setEnd(pTextNode->GetText().getLength());
897 }
898 else if (pTextNode->HasHints())
899 {
900 SwpHints& rHints = pTextNode->GetSwpHints();
901 for (size_t i = 0; i < rHints.Count(); ++i)
902 {
903 const SwTextAttr* pTextAttr = rHints.Get(i);
904 if (pTextAttr->Which() == RES_TXTATR_AUTOFMT)
905 {
906 checkAutoFormat(pTextNode, pTextAttr);
907 }
908 }
909 }
910 }
911};
912
913class FakeCaptionCheck : public NodeCheck
914{
915public:
916 FakeCaptionCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
917 : NodeCheck(rIssueCollection)
918 {
919 }
920 void check(SwNode* pCurrent) override
921 {
922 if (!pCurrent->IsTextNode())
923 return;
924
925 SwTextNode* pTextNode = pCurrent->GetTextNode();
926 const OUString& sText = pTextNode->GetText();
927
928 if (sText.getLength() == 0)
929 return;
930
931 // Check if it's a real caption
932 const SwNode* aStartFly = pCurrent->FindFlyStartNode();
933 if (aStartFly
934 && aStartFly->GetFlyFormat()->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
935 return;
936
938 auto nCount = 0;
939 for (auto aTextFrame = aIter.First(); aTextFrame; aTextFrame = aIter.Next())
940 {
941 auto aObjects = aTextFrame->GetDrawObjs();
942 if (aObjects)
943 nCount += aObjects->size();
944
945 if (nCount > 1)
946 return;
947 }
948
949 // Check that there's exactly 1 image anchored in this node
950 if (nCount == 1)
951 {
952 OString sTemp;
953 sText.convertToString(&sTemp, RTL_TEXTENCODING_ASCII_US, 0);
954 if (sText.startsWith(SwResId(STR_POOLCOLL_LABEL))
955 || sText.startsWith(SwResId(STR_POOLCOLL_LABEL_ABB))
956 || sText.startsWith(SwResId(STR_POOLCOLL_LABEL_TABLE))
957 || sText.startsWith(SwResId(STR_POOLCOLL_LABEL_FRAME))
958 || sText.startsWith(SwResId(STR_POOLCOLL_LABEL_DRAWING))
959 || sText.startsWith(SwResId(STR_POOLCOLL_LABEL_FIGURE)))
960 {
961 auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_FAKE_CAPTIONS),
963 pIssue->setIssueObject(IssueObject::TEXT);
964 pIssue->setNode(pTextNode);
965 SwDoc& rDocument = pTextNode->GetDoc();
966 pIssue->setDoc(rDocument);
967 pIssue->setStart(0);
968 pIssue->setEnd(sText.getLength());
969 }
970 }
971 }
972};
973
974class BlinkingTextCheck : public NodeCheck
975{
976private:
977 void checkTextRange(uno::Reference<text::XTextRange> const& xTextRange)
978 {
979 uno::Reference<beans::XPropertySet> xProperties(xTextRange, uno::UNO_QUERY);
980 if (xProperties.is() && xProperties->getPropertySetInfo()->hasPropertyByName("CharFlash"))
981 {
982 bool bBlinking = false;
983 xProperties->getPropertyValue("CharFlash") >>= bBlinking;
984
985 if (bBlinking)
986 {
987 lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_BLINKING));
988 }
989 }
990 }
991
992public:
993 BlinkingTextCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
994 : NodeCheck(rIssueCollection)
995 {
996 }
997
998 void check(SwNode* pCurrent) override
999 {
1000 if (!pCurrent->IsTextNode())
1001 return;
1002
1003 SwTextNode* pTextNode = pCurrent->GetTextNode();
1004 uno::Reference<text::XTextContent> xParagraph;
1005 xParagraph = SwXParagraph::CreateXParagraph(pTextNode->GetDoc(), pTextNode);
1006 if (!xParagraph.is())
1007 return;
1008
1009 uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph, uno::UNO_QUERY);
1010 uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
1011 while (xRunEnum->hasMoreElements())
1012 {
1013 uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
1014 if (xRun.is())
1015 checkTextRange(xRun);
1016 }
1017 }
1018};
1019
1020class HeaderCheck : public NodeCheck
1021{
1022private:
1024
1025public:
1026 HeaderCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
1027 : NodeCheck(rIssueCollection)
1028 , m_nPreviousLevel(0)
1029 {
1030 }
1031
1032 void check(SwNode* pCurrent) override
1033 {
1034 if (!pCurrent->IsTextNode())
1035 return;
1036
1037 SwTextNode* pTextNode = pCurrent->GetTextNode();
1038 SwTextFormatColl* pCollection = pTextNode->GetTextColl();
1039 if (!pCollection->IsAssignedToListLevelOfOutlineStyle())
1040 return;
1041
1042 int nLevel = pCollection->GetAssignedOutlineStyleLevel();
1043 assert(nLevel >= 0);
1044 if (nLevel > m_nPreviousLevel && std::abs(nLevel - m_nPreviousLevel) > 1)
1045 {
1046 lclAddIssue(m_rIssueCollection, SwResId(STR_HEADINGS_NOT_IN_ORDER));
1047 }
1048 m_nPreviousLevel = nLevel;
1049 }
1050};
1051
1052// ISO 142891-1 : 7.14
1053class NonInteractiveFormCheck : public NodeCheck
1054{
1055public:
1056 NonInteractiveFormCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
1057 : NodeCheck(rIssueCollection)
1058 {
1059 }
1060
1061 void check(SwNode* pCurrent) override
1062 {
1063 if (!pCurrent->IsTextNode())
1064 return;
1065
1066 const auto& text = pCurrent->GetTextNode()->GetText();
1067
1068 // Series of tests to detect if there are fake forms in the text.
1069
1070 bool bCheck = text.indexOf("___") == -1; // Repeated underscores.
1071
1072 if (bCheck)
1073 bCheck = text.indexOf("....") == -1; // Repeated dots.
1074
1075 if (bCheck)
1076 bCheck = text.indexOf(u"……") == -1; // Repeated ellipsis.
1077
1078 if (bCheck)
1079 bCheck = text.indexOf(u"….") == -1; // A dot after an ellipsis.
1080
1081 if (bCheck)
1082 bCheck = text.indexOf(u".…") == -1; // An ellipsis after a dot.
1083
1084 // Checking if all the tests are passed successfully. If not, adding a warning.
1085 if (!bCheck)
1086 lclAddIssue(m_rIssueCollection, SwResId(STR_NON_INTERACTIVE_FORMS));
1087 }
1088};
1089
1091class FloatingTextCheck : public NodeCheck
1092{
1093public:
1094 FloatingTextCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
1095 : NodeCheck(rIssueCollection)
1096 {
1097 }
1098
1099 void check(SwNode* pCurrent) override
1100 {
1101 // if node is a text-node and if it has text, we proceed. Otherwise - return.
1102 const SwTextNode* textNode = pCurrent->GetTextNode();
1103 if (!textNode || textNode->GetText().isEmpty())
1104 return;
1105
1106 // If a node is in fly and if it is not anchored as char, throw warning.
1107 const SwNode* startFly = pCurrent->FindFlyStartNode();
1108 if (startFly
1109 && startFly->GetFlyFormat()->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
1110 lclAddIssue(m_rIssueCollection, SwResId(STR_FLOATING_TEXT));
1111 }
1112};
1113
1115class TableHeadingCheck : public NodeCheck
1116{
1117private:
1118 // Boolean indicating if heading-in-table warning is already triggered.
1120
1121public:
1122 TableHeadingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
1123 : NodeCheck(rIssueCollection)
1124 , m_bPrevPassed(true)
1125 {
1126 }
1127
1128 void check(SwNode* pCurrent) override
1129 {
1130 if (!m_bPrevPassed)
1131 return;
1132
1133 const SwTextNode* textNode = pCurrent->GetTextNode();
1134
1135 if (textNode && textNode->GetAttrOutlineLevel() != 0)
1136 {
1137 const SwTableNode* parentTable = pCurrent->FindTableNode();
1138
1139 if (parentTable)
1140 {
1141 m_bPrevPassed = false;
1142 lclAddIssue(m_rIssueCollection, SwResId(STR_HEADING_IN_TABLE));
1143 }
1144 }
1145 }
1146};
1147
1149class HeadingOrderCheck : public NodeCheck
1150{
1151public:
1152 HeadingOrderCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
1153 : NodeCheck(rIssueCollection)
1154 {
1155 }
1156
1157 void check(SwNode* pCurrent) override
1158 {
1159 const SwTextNode* pTextNode = pCurrent->GetTextNode();
1160 if (!pTextNode)
1161 return;
1162
1163 // If outline level stands for heading level...
1164 const int currentLevel = pTextNode->GetAttrOutlineLevel();
1165 if (!currentLevel)
1166 return;
1167
1168 // ... and if is bigger than previous by more than 1, warn.
1169 if (currentLevel - m_prevLevel > 1)
1170 {
1171 // Preparing and posting a warning.
1172 OUString resultString = SwResId(STR_HEADING_ORDER);
1173 resultString
1174 = resultString.replaceAll("%LEVEL_CURRENT%", OUString::number(currentLevel));
1175 resultString = resultString.replaceAll("%LEVEL_PREV%", OUString::number(m_prevLevel));
1176
1177 lclAddIssue(m_rIssueCollection, resultString);
1178 }
1179
1180 // Updating previous level.
1181 m_prevLevel = currentLevel;
1182 }
1183
1184private:
1185 // Previous heading level to compare with.
1187};
1188
1189class DocumentCheck : public BaseCheck
1190{
1191public:
1192 DocumentCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
1193 : BaseCheck(rIssueCollection)
1194 {
1195 }
1196
1197 virtual void check(SwDoc* pDoc) = 0;
1198};
1199
1200// Check default language
1201class DocumentDefaultLanguageCheck : public DocumentCheck
1202{
1203public:
1204 DocumentDefaultLanguageCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
1205 : DocumentCheck(rIssueCollection)
1206 {
1207 }
1208
1209 void check(SwDoc* pDoc) override
1210 {
1211 // TODO maybe - also check RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE if CJK or CTL are enabled
1212 const SvxLanguageItem& rLang = pDoc->GetDefault(RES_CHRATR_LANGUAGE);
1213 LanguageType eLanguage = rLang.GetLanguage();
1214 if (eLanguage == LANGUAGE_NONE)
1215 {
1216 lclAddIssue(m_rIssueCollection, SwResId(STR_DOCUMENT_DEFAULT_LANGUAGE),
1218 }
1219 else
1220 {
1221 for (SwTextFormatColl* pTextFormatCollection : *pDoc->GetTextFormatColls())
1222 {
1223 const SwAttrSet& rAttrSet = pTextFormatCollection->GetAttrSet();
1224 if (rAttrSet.GetLanguage(false).GetLanguage() == LANGUAGE_NONE)
1225 {
1226 OUString sName = pTextFormatCollection->GetName();
1227 OUString sIssueText
1228 = SwResId(STR_STYLE_NO_LANGUAGE).replaceAll("%STYLE_NAME%", sName);
1229 lclAddIssue(m_rIssueCollection, sIssueText,
1231 }
1232 }
1233 }
1234 }
1235};
1236
1237class DocumentTitleCheck : public DocumentCheck
1238{
1239public:
1240 DocumentTitleCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
1241 : DocumentCheck(rIssueCollection)
1242 {
1243 }
1244
1245 void check(SwDoc* pDoc) override
1246 {
1247 SwDocShell* pShell = pDoc->GetDocShell();
1248 if (!pShell)
1249 return;
1250
1251 const uno::Reference<document::XDocumentPropertiesSupplier> xDPS(pShell->GetModel(),
1252 uno::UNO_QUERY_THROW);
1253 const uno::Reference<document::XDocumentProperties> xDocumentProperties(
1254 xDPS->getDocumentProperties());
1255 OUString sTitle = xDocumentProperties->getTitle();
1256 if (sTitle.trim().isEmpty())
1257 {
1258 lclAddIssue(m_rIssueCollection, SwResId(STR_DOCUMENT_TITLE),
1260 }
1261 }
1262};
1263
1264class FootnoteEndnoteCheck : public DocumentCheck
1265{
1266public:
1267 FootnoteEndnoteCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
1268 : DocumentCheck(rIssueCollection)
1269 {
1270 }
1271
1272 void check(SwDoc* pDoc) override
1273 {
1274 for (SwTextFootnote const* pTextFootnote : pDoc->GetFootnoteIdxs())
1275 {
1276 SwFormatFootnote const& rFootnote = pTextFootnote->GetFootnote();
1277 if (rFootnote.IsEndNote())
1278 {
1279 lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_ENDNOTES));
1280 }
1281 else
1282 {
1283 lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_FOOTNOTES));
1284 }
1285 }
1286 }
1287};
1288
1289class BackgroundImageCheck : public DocumentCheck
1290{
1291public:
1292 BackgroundImageCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
1293 : DocumentCheck(rIssueCollection)
1294 {
1295 }
1296 void check(SwDoc* pDoc) override
1297 {
1298 uno::Reference<lang::XComponent> xDoc = pDoc->GetDocShell()->GetBaseModel();
1299 uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xDoc, uno::UNO_QUERY);
1300 if (!xStyleFamiliesSupplier.is())
1301 return;
1302 uno::Reference<container::XNameAccess> xStyleFamilies
1303 = xStyleFamiliesSupplier->getStyleFamilies();
1304 uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"),
1305 uno::UNO_QUERY);
1306 if (!xStyleFamily.is())
1307 return;
1308 const uno::Sequence<OUString>& xStyleFamilyNames = xStyleFamily->getElementNames();
1309 for (const OUString& rStyleFamilyName : xStyleFamilyNames)
1310 {
1311 uno::Reference<beans::XPropertySet> xPropertySet(
1312 xStyleFamily->getByName(rStyleFamilyName), uno::UNO_QUERY);
1313 if (!xPropertySet.is())
1314 continue;
1315 auto aFillStyleContainer = xPropertySet->getPropertyValue("FillStyle");
1316 if (aFillStyleContainer.has<drawing::FillStyle>())
1317 {
1318 drawing::FillStyle aFillStyle = aFillStyleContainer.get<drawing::FillStyle>();
1319 if (aFillStyle == drawing::FillStyle_BITMAP)
1320 {
1321 lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_BACKGROUND_IMAGES),
1323 }
1324 }
1325 }
1326 }
1327};
1328
1329} // end anonymous namespace
1330
1331// Check Shapes, TextBox
1333{
1334 if (!pObject)
1335 return;
1336
1337 // Check for fontworks.
1338 if (SdrObjCustomShape* pCustomShape = dynamic_cast<SdrObjCustomShape*>(pObject))
1339 {
1340 const SdrCustomShapeGeometryItem& rGeometryItem
1341 = pCustomShape->GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY);
1342
1343 if (const uno::Any* pAny = rGeometryItem.GetPropertyValueByName("Type"))
1344 if (pAny->get<OUString>().startsWith("fontwork-"))
1345 lclAddIssue(m_aIssueCollection, SwResId(STR_FONTWORKS));
1346 }
1347
1348 // Checking if there is floating Writer text draw object and if so, throwing a warning.
1349 // (Floating objects with text create problems with reading order)
1350 if (pObject->HasText()
1351 && FindFrameFormat(pObject)->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
1352 lclAddIssue(m_aIssueCollection, SwResId(STR_FLOATING_TEXT));
1353
1354 if (pObject->GetObjIdentifier() == SdrObjKind::CustomShape
1355 || pObject->GetObjIdentifier() == SdrObjKind::Text)
1356 {
1357 OUString sAlternative = pObject->GetTitle();
1358 if (sAlternative.isEmpty())
1359 {
1360 OUString sName = pObject->GetName();
1361 OUString sIssueText = SwResId(STR_NO_ALT).replaceAll("%OBJECT_NAME%", sName);
1363 }
1364 }
1365}
1366
1368{
1369 if (m_aDocumentChecks.empty())
1370 {
1371 m_aDocumentChecks.emplace_back(new DocumentDefaultLanguageCheck(m_aIssueCollection));
1372 m_aDocumentChecks.emplace_back(new DocumentTitleCheck(m_aIssueCollection));
1373 m_aDocumentChecks.emplace_back(new FootnoteEndnoteCheck(m_aIssueCollection));
1374 m_aDocumentChecks.emplace_back(new BackgroundImageCheck(m_aIssueCollection));
1375 }
1376
1377 if (m_aNodeChecks.empty())
1378 {
1379 m_aNodeChecks.emplace_back(new NoTextNodeAltTextCheck(m_aIssueCollection));
1380 m_aNodeChecks.emplace_back(new TableNodeMergeSplitCheck(m_aIssueCollection));
1381 m_aNodeChecks.emplace_back(new TableFormattingCheck(m_aIssueCollection));
1382 m_aNodeChecks.emplace_back(new NumberingCheck(m_aIssueCollection));
1383 m_aNodeChecks.emplace_back(new HyperlinkCheck(m_aIssueCollection));
1384 m_aNodeChecks.emplace_back(new TextContrastCheck(m_aIssueCollection));
1385 m_aNodeChecks.emplace_back(new BlinkingTextCheck(m_aIssueCollection));
1386 m_aNodeChecks.emplace_back(new HeaderCheck(m_aIssueCollection));
1387 m_aNodeChecks.emplace_back(new TextFormattingCheck(m_aIssueCollection));
1388 m_aNodeChecks.emplace_back(new NonInteractiveFormCheck(m_aIssueCollection));
1389 m_aNodeChecks.emplace_back(new FloatingTextCheck(m_aIssueCollection));
1390 m_aNodeChecks.emplace_back(new TableHeadingCheck(m_aIssueCollection));
1391 m_aNodeChecks.emplace_back(new HeadingOrderCheck(m_aIssueCollection));
1392 m_aNodeChecks.emplace_back(new NewlineSpacingCheck(m_aIssueCollection));
1393 m_aNodeChecks.emplace_back(new SpaceSpacingCheck(m_aIssueCollection));
1394 m_aNodeChecks.emplace_back(new FakeFootnoteCheck(m_aIssueCollection));
1395 m_aNodeChecks.emplace_back(new FakeCaptionCheck(m_aIssueCollection));
1396 }
1397}
1398
1400{
1401 if (m_pDoc == nullptr || pNode == nullptr)
1402 return;
1403
1404 init();
1405
1406 for (std::shared_ptr<BaseCheck>& rpNodeCheck : m_aNodeChecks)
1407 {
1408 auto pNodeCheck = dynamic_cast<NodeCheck*>(rpNodeCheck.get());
1409 if (pNodeCheck)
1410 pNodeCheck->check(pNode);
1411 }
1412}
1413
1415{
1416 if (m_pDoc == nullptr)
1417 return;
1418
1419 init();
1420
1421 for (std::shared_ptr<BaseCheck>& rpDocumentCheck : m_aDocumentChecks)
1422 {
1423 auto pDocumentCheck = dynamic_cast<DocumentCheck*>(rpDocumentCheck.get());
1424 if (pDocumentCheck)
1425 pDocumentCheck->check(m_pDoc);
1426 }
1427}
1428
1430{
1431 if (m_pDoc == nullptr)
1432 return;
1433
1434 init();
1435
1437
1438 auto const& pNodes = m_pDoc->GetNodes();
1439 SwNode* pNode = nullptr;
1440 for (SwNodeOffset n(0); n < pNodes.Count(); ++n)
1441 {
1442 pNode = pNodes[n];
1443 if (pNode)
1444 {
1445 for (std::shared_ptr<BaseCheck>& rpNodeCheck : m_aNodeChecks)
1446 {
1447 auto pNodeCheck = dynamic_cast<NodeCheck*>(rpNodeCheck.get());
1448 if (pNodeCheck)
1449 pNodeCheck->check(pNode);
1450 }
1451 }
1452 }
1453
1455 auto* pModel = rDrawModelAccess.GetDrawModel();
1456 for (sal_uInt16 nPage = 0; nPage < pModel->GetPageCount(); ++nPage)
1457 {
1458 SdrPage* pPage = pModel->GetPage(nPage);
1459 for (size_t nObject = 0; nObject < pPage->GetObjCount(); ++nObject)
1460 {
1461 SdrObject* pObject = pPage->GetObj(nObject);
1462 if (pObject)
1464 }
1465 }
1466}
1467
1468} // end sw namespace
1469
1470/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool m_bPrevPassed
int m_nPreviousLevel
const std::vector< std::pair< OUString, OUString > > m_aNumberingCombinations
int m_prevLevel
basegfx::BColor getBColor() const
virtual const SwDrawModel * GetDrawModel() const =0
Draw Model and id accessors.
css::uno::Any * GetPropertyValueByName(const OUString &rPropName)
SdrObject * GetObj(size_t nNum) const
size_t GetObjCount() const
const SfxPoolItem * GetItem(sal_uInt16 nWhich, bool bSearchInParent=true) const
css::uno::Reference< css::frame::XModel3 > GetModel() const
css::uno::Reference< css::frame::XModel3 > GetBaseModel() const
sal_uInt16 Which() const
LanguageType GetLanguage() const
const SvxLanguageItem & GetLanguage(bool=true) const
Definition: charatr.hxx:91
bool HasSwAttrSet() const
Definition: node.hxx:494
const SwAttrSet & GetSwAttrSet() const
Does node has already its own auto-attributes? Access to SwAttrSet.
Definition: node.hxx:763
Definition: doc.hxx:194
SwNodes & GetNodes()
Definition: doc.hxx:417
SwFootnoteIdxs & GetFootnoteIdxs()
Definition: doc.hxx:642
const SfxPoolItem & GetDefault(sal_uInt16 nFormatHint) const
Get the default attribute in this document.
Definition: docfmt.cxx:664
const SwTextFormatColls * GetTextFormatColls() const
Definition: doc.hxx:786
IDocumentDrawModelAccess const & getIDocumentDrawModelAccess() const
Definition: doc.cxx:161
SwDocShell * GetDocShell()
Definition: doc.hxx:1359
RndStdIds GetAnchorId() const
Definition: fmtanchr.hxx:67
const std::shared_ptr< SfxItemSet > & GetStyleHandle() const
Definition: fmtautofmt.hxx:49
bool IsEndNote() const
Definition: fmtftn.hxx:73
const OUString & GetName() const
Definition: format.hxx:131
const SwFormatAnchor & GetAnchor(bool=true) const
Definition: fmtanchr.hxx:83
const SwAttrSet & GetAttrSet() const
For querying the attribute array.
Definition: format.hxx:136
Style of a layout element.
Definition: frmfmt.hxx:62
Layout frame for SwNoTextNode, i.e. graphics and OLE nodes (including charts).
Definition: ndnotxt.hxx:30
OUString GetTitle() const
Definition: ndnotxt.cxx:258
Base class of the Writer document model elements.
Definition: node.hxx:98
bool IsGrfNode() const
Definition: node.hxx:705
SwFrameFormat * GetFlyFormat() const
If node is in a fly return the respective format.
Definition: node.cxx:742
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:897
SwNodeOffset GetIndex() const
Definition: node.hxx:312
SwNodes & GetNodes()
Node is in which nodes-array/doc?
Definition: node.hxx:742
SwTableBox * GetTableBox() const
If node is in a table return the respective table box.
Definition: node.cxx:777
SwDoc & GetDoc()
Definition: node.hxx:233
const SwStartNode * FindFlyStartNode() const
Definition: node.hxx:220
const SwPageDesc * FindPageDesc(SwNodeOffset *pPgDescNdIdx=nullptr) const
Search PageDesc with which this node is formatted.
Definition: node.cxx:498
bool IsTextNode() const
Definition: node.hxx:685
SwTableNode * FindTableNode()
Search table node, in which it is.
Definition: node.cxx:381
SwNoTextNode * GetNoTextNode()
Definition: ndnotxt.hxx:95
SwTableNode * GetTableNode()
Definition: node.hxx:648
SwNodeType GetNodeType() const
Definition: node.hxx:166
bool IsOLENode() const
Definition: node.hxx:701
SwNodeOffset Count() const
Definition: ndarr.hxx:142
SwFrameFormat & GetMaster()
Definition: pagedesc.hxx:238
SwTableBox is one table cell in the document model.
Definition: swtable.hxx:426
SwTableLine is one table row in the document model.
Definition: swtable.hxx:364
size_type size() const
Definition: swtable.hxx:76
const SwTable & GetTable() const
Definition: node.hxx:542
SwTable is one table in the document model, containing rows (which contain cells).
Definition: swtable.hxx:113
SwTableLines & GetTabLines()
Definition: swtable.hxx:204
SwTableFormat * GetFrameFormat()
Definition: swtable.hxx:207
bool IsTableComplex() const
Definition: swtable.cxx:1441
A wrapper around SfxPoolItem to store the start position of (usually) a text portion,...
Definition: txatbase.hxx:44
sal_Int32 GetAnyEnd() const
end (if available), else start
Definition: txatbase.hxx:161
sal_Int32 GetStart() const
Definition: txatbase.hxx:88
const SwFormatAutoFormat & GetAutoFormat() const
Definition: txatbase.hxx:193
sal_uInt16 Which() const
Definition: txatbase.hxx:116
Represents the style of a paragraph.
Definition: fmtcol.hxx:59
bool IsAssignedToListLevelOfOutlineStyle() const
Definition: fmtcol.hxx:120
int GetAssignedOutlineStyleLevel() const
Definition: fmtcol.cxx:617
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:111
bool HasHints() const
Definition: ndtxt.hxx:252
SwpHints & GetSwpHints()
getters for SwpHints
Definition: ndtxt.hxx:863
int GetAttrOutlineLevel() const
Returns outline level of this text node.
Definition: ndtxt.cxx:4148
const OUString & GetText() const
Definition: ndtxt.hxx:242
SwTextFormatColl * GetTextColl() const
Definition: ndtxt.hxx:891
static rtl::Reference< SwXParagraph > CreateXParagraph(SwDoc &rDoc, SwTextNode *pTextNode, css::uno::Reference< css::text::XText > const &xParentText=nullptr, const sal_Int32 nSelStart=-1, const sal_Int32 nSelEnd=- 1)
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:68
SwTextAttr * Get(size_t nPos) const
Definition: ndhints.hxx:144
size_t Count() const
Definition: ndhints.hxx:142
const Color & GetColorValue() const
double getBlue() const
double getRed() const
double getGreen() const
AccessibilityIssueCollection m_aIssueCollection
std::vector< std::shared_ptr< AccessibilityIssue > > & getIssues()
std::vector< std::shared_ptr< BaseCheck > > m_aDocumentChecks
void checkNode(SwNode *pNode)
void checkObject(SdrObject *pObject)
std::vector< std::shared_ptr< BaseCheck > > m_aNodeChecks
Base class for accessibility checks.
constexpr ::Color COL_WHITE(0xFF, 0xFF, 0xFF)
int nCount
SwFrameFormat * FindFrameFormat(SdrObject *pObj)
The Get reverse way: seeks the format to the specified object.
Definition: dcontact.cxx:121
EmbeddedObjectRef * pObject
constexpr TypedWhichId< SvxFontHeightItem > RES_CHRATR_CTL_FONTSIZE(28)
constexpr TypedWhichId< SvxCrossedOutItem > RES_CHRATR_CROSSEDOUT(5)
constexpr TypedWhichId< SvxFontItem > RES_CHRATR_CJK_FONT(22)
constexpr TypedWhichId< SvxUnderlineItem > RES_CHRATR_UNDERLINE(14)
constexpr TypedWhichId< SvxFontHeightItem > RES_CHRATR_FONTSIZE(8)
constexpr TypedWhichId< SvxLanguageItem > RES_CHRATR_LANGUAGE(10)
constexpr TypedWhichId< SvxWeightItem > RES_CHRATR_WEIGHT(15)
constexpr TypedWhichId< SvxShadowedItem > RES_CHRATR_SHADOWED(13)
constexpr TypedWhichId< SvxFontHeightItem > RES_CHRATR_CJK_FONTSIZE(23)
constexpr TypedWhichId< SvxFontItem > RES_CHRATR_CTL_FONT(27)
constexpr TypedWhichId< SwFormatAutoFormat > RES_TXTATR_AUTOFMT(50)
constexpr TypedWhichId< SvxWeightItem > RES_CHRATR_CTL_WEIGHT(31)
constexpr TypedWhichId< SvxContourItem > RES_CHRATR_CONTOUR(4)
constexpr TypedWhichId< SvxCharReliefItem > RES_CHRATR_RELIEF(36)
constexpr TypedWhichId< SvxEscapementItem > RES_CHRATR_ESCAPEMENT(6)
constexpr TypedWhichId< SvxPostureItem > RES_CHRATR_CTL_POSTURE(30)
constexpr TypedWhichId< SvxEmphasisMarkItem > RES_CHRATR_EMPHASIS_MARK(33)
constexpr TypedWhichId< SvxPostureItem > RES_CHRATR_POSTURE(11)
constexpr TypedWhichId< SvxOverlineItem > RES_CHRATR_OVERLINE(38)
constexpr TypedWhichId< SvxWeightItem > RES_CHRATR_CJK_WEIGHT(26)
constexpr TypedWhichId< SvxFontItem > RES_CHRATR_FONT(7)
constexpr TypedWhichId< SvxPostureItem > RES_CHRATR_CJK_POSTURE(25)
constexpr TypedWhichId< SvxColorItem > RES_CHRATR_COLOR(3)
sal_Int32 nIndex
sal_Int64 n
#define SAL_WARN(area, stream)
const char * sName
def text(shape, orig_st)
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > > check(dp_misc::DescriptionInfoset const &infoset)
int i
void remove_duplicates(std::vector< T > &rVector)
AccessibilityIssueID
Dialog to specify the properties of date form field.
@ Table
SwTableNode is derived from SwStartNode.
SwNodeOffset abs(const SwNodeOffset &a)
Definition: nodeoffset.hxx:34
constexpr TypedWhichId< SdrCustomShapeGeometryItem > SDRATTR_CUSTOMSHAPE_GEOMETRY(SDRATTR_CUSTOMSHAPE_FIRST+2)
OUString SwResId(TranslateId aId)
Definition: swmodule.cxx:165
constexpr TypedWhichId< XFillColorItem > XATTR_FILLCOLOR(XATTR_FILL_FIRST+1)