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 <ndgrf.hxx>
15 #include <ndole.hxx>
16 #include <ndtxt.hxx>
17 #include <docsh.hxx>
19 #include <drawdoc.hxx>
20 #include <svx/svdpage.hxx>
21 #include <swtable.hxx>
22 #include <com/sun/star/text/XTextContent.hpp>
23 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
24 #include <unoparagraph.hxx>
25 #include <tools/urlobj.hxx>
26 #include <editeng/langitem.hxx>
27 #include <charatr.hxx>
28 #include <svx/xfillit0.hxx>
29 #include <svx/xflclit.hxx>
30 #include <ftnidx.hxx>
31 #include <txtftn.hxx>
32 #include <svl/itemiter.hxx>
33 #include <o3tl/vector_utils.hxx>
34 
35 namespace sw
36 {
37 namespace
38 {
39 std::shared_ptr<sw::AccessibilityIssue>
40 lclAddIssue(sfx::AccessibilityIssueCollection& rIssueCollection, OUString const& rText,
42 {
43  auto pIssue = std::make_shared<sw::AccessibilityIssue>(eIssue);
44  pIssue->m_aIssueText = rText;
45  rIssueCollection.getIssues().push_back(pIssue);
46  return pIssue;
47 }
48 
49 class BaseCheck
50 {
51 protected:
53 
54 public:
55  BaseCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
56  : m_rIssueCollection(rIssueCollection)
57  {
58  }
59  virtual ~BaseCheck() {}
60 };
61 
62 class NodeCheck : public BaseCheck
63 {
64 public:
65  NodeCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
66  : BaseCheck(rIssueCollection)
67  {
68  }
69 
70  virtual void check(SwNode* pCurrent) = 0;
71 };
72 
73 // Check NoTextNodes: Graphic, OLE for alt (title) text
74 class NoTextNodeAltTextCheck : public NodeCheck
75 {
76  void checkNoTextNode(SwNoTextNode* pNoTextNode)
77  {
78  if (!pNoTextNode)
79  return;
80 
81  OUString sAlternative = pNoTextNode->GetTitle();
82  if (sAlternative.isEmpty())
83  {
84  OUString sName = pNoTextNode->GetFlyFormat()->GetName();
85 
86  OUString sIssueText = SwResId(STR_NO_ALT).replaceAll("%OBJECT_NAME%", sName);
87 
88  if (pNoTextNode->IsOLENode())
89  {
90  auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
92  pIssue->setDoc(pNoTextNode->GetDoc());
93  pIssue->setIssueObject(IssueObject::OLE);
94  pIssue->setObjectID(pNoTextNode->GetFlyFormat()->GetName());
95  }
96  else if (pNoTextNode->IsGrfNode())
97  {
98  auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
100  pIssue->setDoc(pNoTextNode->GetDoc());
101  pIssue->setIssueObject(IssueObject::GRAPHIC);
102  pIssue->setObjectID(pNoTextNode->GetFlyFormat()->GetName());
103  }
104  }
105  }
106 
107 public:
108  NoTextNodeAltTextCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
109  : NodeCheck(rIssueCollection)
110  {
111  }
112 
113  void check(SwNode* pCurrent) override
114  {
115  if (pCurrent->GetNodeType() & SwNodeType::NoTextMask)
116  {
117  SwNoTextNode* pNoTextNode = pCurrent->GetNoTextNode();
118  if (pNoTextNode)
119  checkNoTextNode(pNoTextNode);
120  }
121  }
122 };
123 
124 // Check Table node if the table is merged and split.
125 class TableNodeMergeSplitCheck : public NodeCheck
126 {
127 private:
128  void addTableIssue(SwTable const& rTable, SwDoc* pDoc)
129  {
130  const SwTableFormat* pFormat = rTable.GetFrameFormat();
131  OUString sName = pFormat->GetName();
132  OUString sIssueText = SwResId(STR_TABLE_MERGE_SPLIT).replaceAll("%OBJECT_NAME%", sName);
133  auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
135  pIssue->setDoc(pDoc);
136  pIssue->setIssueObject(IssueObject::TABLE);
137  pIssue->setObjectID(sName);
138  }
139 
140  void checkTableNode(SwTableNode* pTableNode)
141  {
142  if (!pTableNode)
143  return;
144 
145  SwTable const& rTable = pTableNode->GetTable();
146  SwDoc* pDoc = pTableNode->GetDoc();
147  if (rTable.IsTableComplex())
148  {
149  addTableIssue(rTable, pDoc);
150  }
151  else
152  {
153  if (rTable.GetTabLines().size() > 1)
154  {
155  int i = 0;
156  size_t nFirstLineSize = 0;
157  bool bAllColumnsSameSize = true;
158  bool bCellSpansOverMoreRows = false;
159 
160  for (SwTableLine const* pTableLine : rTable.GetTabLines())
161  {
162  if (i == 0)
163  {
164  nFirstLineSize = pTableLine->GetTabBoxes().size();
165  }
166  else
167  {
168  size_t nLineSize = pTableLine->GetTabBoxes().size();
169  if (nFirstLineSize != nLineSize)
170  {
171  bAllColumnsSameSize = false;
172  }
173  }
174  i++;
175 
176  // Check for row span in each table box (cell)
177  for (SwTableBox const* pBox : pTableLine->GetTabBoxes())
178  {
179  if (pBox->getRowSpan() > 1)
180  bCellSpansOverMoreRows = true;
181  }
182  }
183  if (!bAllColumnsSameSize || bCellSpansOverMoreRows)
184  {
185  addTableIssue(rTable, pDoc);
186  }
187  }
188  }
189  }
190 
191 public:
192  TableNodeMergeSplitCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
193  : NodeCheck(rIssueCollection)
194  {
195  }
196 
197  void check(SwNode* pCurrent) override
198  {
199  if (pCurrent->GetNodeType() & SwNodeType::Table)
200  {
201  SwTableNode* pTableNode = pCurrent->GetTableNode();
202  if (pTableNode)
203  checkTableNode(pTableNode);
204  }
205  }
206 };
207 
208 class NumberingCheck : public NodeCheck
209 {
210 private:
212 
213  const std::vector<std::pair<OUString, OUString>> m_aNumberingCombinations{
214  { "1.", "2." }, { "(1)", "(2)" }, { "1)", "2)" }, { "a.", "b." }, { "(a)", "(b)" },
215  { "a)", "b)" }, { "A.", "B." }, { "(A)", "(B)" }, { "A)", "B)" }
216  };
217 
218 public:
219  NumberingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
220  : NodeCheck(rIssueCollection)
221  , m_pPreviousTextNode(nullptr)
222  {
223  }
224 
225  void check(SwNode* pCurrent) override
226  {
227  if (pCurrent->IsTextNode())
228  {
229  if (m_pPreviousTextNode)
230  {
231  for (auto& rPair : m_aNumberingCombinations)
232  {
233  if (pCurrent->GetTextNode()->GetText().startsWith(rPair.second)
234  && m_pPreviousTextNode->GetText().startsWith(rPair.first))
235  {
236  OUString sNumbering = rPair.first + " " + rPair.second + "...";
237  OUString sIssueText
238  = SwResId(STR_FAKE_NUMBERING).replaceAll("%NUMBERING%", sNumbering);
239  lclAddIssue(m_rIssueCollection, sIssueText);
240  }
241  }
242  }
243  m_pPreviousTextNode = pCurrent->GetTextNode();
244  }
245  }
246 };
247 
248 class HyperlinkCheck : public NodeCheck
249 {
250 private:
251  void checkTextRange(uno::Reference<text::XTextRange> const& xTextRange)
252  {
253  uno::Reference<beans::XPropertySet> xProperties(xTextRange, uno::UNO_QUERY);
254  if (xProperties->getPropertySetInfo()->hasPropertyByName("HyperLinkURL"))
255  {
256  OUString sHyperlink;
257  xProperties->getPropertyValue("HyperLinkURL") >>= sHyperlink;
258  if (!sHyperlink.isEmpty())
259  {
260  OUString sText = xTextRange->getString();
261  if (INetURLObject(sText) == INetURLObject(sHyperlink))
262  {
263  OUString sIssueText
264  = SwResId(STR_HYPERLINK_TEXT_IS_LINK).replaceFirst("%LINK%", sHyperlink);
265  lclAddIssue(m_rIssueCollection, sIssueText);
266  }
267  }
268  }
269  }
270 
271 public:
272  HyperlinkCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
273  : NodeCheck(rIssueCollection)
274  {
275  }
276 
277  void check(SwNode* pCurrent) override
278  {
279  if (pCurrent->IsTextNode())
280  {
281  SwTextNode* pTextNode = pCurrent->GetTextNode();
282  uno::Reference<text::XTextContent> xParagraph
283  = SwXParagraph::CreateXParagraph(*pTextNode->GetDoc(), pTextNode);
284  if (xParagraph.is())
285  {
286  uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph,
287  uno::UNO_QUERY);
288  uno::Reference<container::XEnumeration> xRunEnum
289  = xRunEnumAccess->createEnumeration();
290  while (xRunEnum->hasMoreElements())
291  {
292  uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
293  if (xRun.is())
294  {
295  checkTextRange(xRun);
296  }
297  }
298  }
299  }
300  }
301 };
302 
303 // Based on https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
304 double calculateRelativeLuminance(Color const& rColor)
305 {
306  // Convert to BColor which has R, G, B colors components
307  // represented by a floating point number from [0.0, 1.0]
308  const basegfx::BColor aBColor = rColor.getBColor();
309 
310  double r = aBColor.getRed();
311  double g = aBColor.getGreen();
312  double b = aBColor.getBlue();
313 
314  // Calculate the values according to the described algorithm
315  r = (r <= 0.03928) ? r / 12.92 : std::pow((r + 0.055) / 1.055, 2.4);
316  g = (g <= 0.03928) ? g / 12.92 : std::pow((g + 0.055) / 1.055, 2.4);
317  b = (b <= 0.03928) ? b / 12.92 : std::pow((b + 0.055) / 1.055, 2.4);
318 
319  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
320 }
321 
322 // TODO move to common color tools (BColorTools maybe)
323 // Based on https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio
324 double calculateContrastRatio(Color const& rColor1, Color const& rColor2)
325 {
326  const double fLuminance1 = calculateRelativeLuminance(rColor1);
327  const double fLuminance2 = calculateRelativeLuminance(rColor2);
328  const std::pair<const double, const double> aMinMax = std::minmax(fLuminance1, fLuminance2);
329 
330  // (L1 + 0.05) / (L2 + 0.05)
331  // L1 is the lighter color (greater luminance value)
332  // L2 is the darker color (smaller luminance value)
333  return (aMinMax.second + 0.05) / (aMinMax.first + 0.05);
334 }
335 
336 class TextContrastCheck : public NodeCheck
337 {
338 private:
339  void checkTextRange(uno::Reference<text::XTextRange> const& xTextRange,
340  uno::Reference<text::XTextContent> const& xParagraph, SwTextNode* pTextNode)
341  {
342  sal_Int32 nParaBackColor = {}; // spurious -Werror=maybe-uninitialized
343  uno::Reference<beans::XPropertySet> xParagraphProperties(xParagraph, uno::UNO_QUERY);
344  if (!(xParagraphProperties->getPropertyValue("ParaBackColor") >>= nParaBackColor))
345  {
346  SAL_WARN("sw.a11y", "ParaBackColor void");
347  return;
348  }
349 
350  uno::Reference<beans::XPropertySet> xProperties(xTextRange, uno::UNO_QUERY);
351  if (xProperties.is())
352  {
353  // Foreground color
354  sal_Int32 nCharColor = {}; // spurious -Werror=maybe-uninitialized
355  if (!(xProperties->getPropertyValue("CharColor") >>= nCharColor))
356  { // not sure this is impossible, can the default be void?
357  SAL_WARN("sw.a11y", "CharColor void");
358  return;
359  }
360  Color aForegroundColor(nCharColor);
361  if (aForegroundColor == COL_AUTO)
362  return;
363 
364  const SwPageDesc* pPageDescription = pTextNode->FindPageDesc();
365  const SwFrameFormat& rPageFormat = pPageDescription->GetMaster();
366  const SwAttrSet& rPageSet = rPageFormat.GetAttrSet();
367 
368  const XFillStyleItem* pXFillStyleItem(
369  rPageSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE, false));
370  Color aPageBackground;
371 
372  if (pXFillStyleItem && pXFillStyleItem->GetValue() == css::drawing::FillStyle_SOLID)
373  {
374  const XFillColorItem* rXFillColorItem
375  = rPageSet.GetItem<XFillColorItem>(XATTR_FILLCOLOR, false);
376  aPageBackground = rXFillColorItem->GetColorValue();
377  }
378 
379  sal_Int32 nCharBackColor = {}; // spurious -Werror=maybe-uninitialized
380 
381  if (!(xProperties->getPropertyValue("CharBackColor") >>= nCharBackColor))
382  {
383  SAL_WARN("sw.a11y", "CharBackColor void");
384  return;
385  }
386  // Determine the background color
387  // Try Character background (highlight)
388  Color aBackgroundColor(nCharBackColor);
389 
390  // If not character background color, try paragraph background color
391  if (aBackgroundColor == COL_AUTO)
392  aBackgroundColor = Color(nParaBackColor);
393 
394  // If not paragraph background color, try page color
395  if (aBackgroundColor == COL_AUTO)
396  aBackgroundColor = aPageBackground;
397 
398  // If not page color, assume white background color
399  if (aBackgroundColor == COL_AUTO)
400  aBackgroundColor = COL_WHITE;
401 
402  double fContrastRatio = calculateContrastRatio(aForegroundColor, aBackgroundColor);
403  if (fContrastRatio < 4.5)
404  {
405  lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_CONTRAST));
406  }
407  }
408  }
409 
410 public:
411  TextContrastCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
412  : NodeCheck(rIssueCollection)
413  {
414  }
415 
416  void check(SwNode* pCurrent) override
417  {
418  if (pCurrent->IsTextNode())
419  {
420  SwTextNode* pTextNode = pCurrent->GetTextNode();
421  uno::Reference<text::XTextContent> xParagraph;
422  xParagraph = SwXParagraph::CreateXParagraph(*pTextNode->GetDoc(), pTextNode);
423  if (xParagraph.is())
424  {
425  uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph,
426  uno::UNO_QUERY);
427  uno::Reference<container::XEnumeration> xRunEnum
428  = xRunEnumAccess->createEnumeration();
429  while (xRunEnum->hasMoreElements())
430  {
431  uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
432  if (xRun.is())
433  checkTextRange(xRun, xParagraph, pTextNode);
434  }
435  }
436  }
437  }
438 };
439 
440 class TextFormattingCheck : public NodeCheck
441 {
442 private:
443 public:
444  TextFormattingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
445  : NodeCheck(rIssueCollection)
446  {
447  }
448 
449  void checkAutoFormat(SwTextNode* pTextNode, const SwTextAttr* pTextAttr)
450  {
451  const SwFormatAutoFormat& rAutoFormat = pTextAttr->GetAutoFormat();
452  SfxItemIter aItemIter(*rAutoFormat.GetStyleHandle());
453  const SfxPoolItem* pItem = aItemIter.GetCurItem();
454  std::vector<OUString> aFormattings;
455  while (pItem)
456  {
457  OUString sFormattingType;
458  switch (pItem->Which())
459  {
460  case RES_CHRATR_WEIGHT:
463  sFormattingType = "Weight";
464  break;
465  case RES_CHRATR_POSTURE:
468  sFormattingType = "Posture";
469  break;
470 
471  case RES_CHRATR_SHADOWED:
472  sFormattingType = "Shadowed";
473  break;
474 
475  case RES_CHRATR_COLOR:
476  sFormattingType = "Font Color";
477  break;
478 
479  case RES_CHRATR_FONTSIZE:
482  sFormattingType = "Font Size";
483  break;
484 
485  case RES_CHRATR_FONT:
486  case RES_CHRATR_CJK_FONT:
487  case RES_CHRATR_CTL_FONT:
488  sFormattingType = "Font";
489  break;
490 
492  sFormattingType = "Emphasis Mark";
493  break;
494 
496  sFormattingType = "Underline";
497  break;
498 
499  case RES_CHRATR_OVERLINE:
500  sFormattingType = "Overline";
501  break;
502 
504  sFormattingType = "Strikethrough";
505  break;
506 
507  case RES_CHRATR_RELIEF:
508  sFormattingType = "Relief";
509  break;
510 
511  case RES_CHRATR_CONTOUR:
512  sFormattingType = "Outline";
513  break;
514  default:
515  break;
516  }
517  if (!sFormattingType.isEmpty())
518  aFormattings.push_back(sFormattingType);
519  pItem = aItemIter.NextItem();
520  }
521  if (!aFormattings.empty())
522  {
523  o3tl::remove_duplicates(aFormattings);
524  auto pIssue
525  = lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_FORMATTING_CONVEYS_MEANING),
527  pIssue->setIssueObject(IssueObject::TEXT);
528  pIssue->setNode(pTextNode);
529  SwDoc* pDocument = pTextNode->GetDoc();
530  pIssue->setDoc(pDocument);
531  pIssue->setStart(pTextAttr->GetStart());
532  pIssue->setEnd(pTextAttr->GetAnyEnd());
533  }
534  }
535  void check(SwNode* pCurrent) override
536  {
537  if (pCurrent->IsTextNode())
538  {
539  SwTextNode* pTextNode = pCurrent->GetTextNode();
540  if (pTextNode->HasHints())
541  {
542  SwpHints& rHints = pTextNode->GetSwpHints();
543  for (size_t i = 0; i < rHints.Count(); ++i)
544  {
545  const SwTextAttr* pTextAttr = rHints.Get(i);
546  if (pTextAttr->Which() == RES_TXTATR_AUTOFMT)
547  {
548  checkAutoFormat(pTextNode, pTextAttr);
549  }
550  }
551  }
552  }
553  }
554 };
555 
556 class BlinkingTextCheck : public NodeCheck
557 {
558 private:
559  void checkTextRange(uno::Reference<text::XTextRange> const& xTextRange)
560  {
561  uno::Reference<beans::XPropertySet> xProperties(xTextRange, uno::UNO_QUERY);
562  if (xProperties.is() && xProperties->getPropertySetInfo()->hasPropertyByName("CharFlash"))
563  {
564  bool bBlinking = false;
565  xProperties->getPropertyValue("CharFlash") >>= bBlinking;
566 
567  if (bBlinking)
568  {
569  lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_BLINKING));
570  }
571  }
572  }
573 
574 public:
575  BlinkingTextCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
576  : NodeCheck(rIssueCollection)
577  {
578  }
579 
580  void check(SwNode* pCurrent) override
581  {
582  if (pCurrent->IsTextNode())
583  {
584  SwTextNode* pTextNode = pCurrent->GetTextNode();
585  uno::Reference<text::XTextContent> xParagraph;
586  xParagraph = SwXParagraph::CreateXParagraph(*pTextNode->GetDoc(), pTextNode);
587  if (xParagraph.is())
588  {
589  uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph,
590  uno::UNO_QUERY);
591  uno::Reference<container::XEnumeration> xRunEnum
592  = xRunEnumAccess->createEnumeration();
593  while (xRunEnum->hasMoreElements())
594  {
595  uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
596  if (xRun.is())
597  checkTextRange(xRun);
598  }
599  }
600  }
601  }
602 };
603 
604 class HeaderCheck : public NodeCheck
605 {
606 private:
608 
609 public:
610  HeaderCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
611  : NodeCheck(rIssueCollection)
612  , m_nPreviousLevel(0)
613  {
614  }
615 
616  void check(SwNode* pCurrent) override
617  {
618  if (pCurrent->IsTextNode())
619  {
620  SwTextNode* pTextNode = pCurrent->GetTextNode();
621  SwTextFormatColl* pCollection = pTextNode->GetTextColl();
622  int nLevel = pCollection->GetAssignedOutlineStyleLevel();
623  if (nLevel < 0)
624  return;
625 
626  if (nLevel > m_nPreviousLevel && std::abs(nLevel - m_nPreviousLevel) > 1)
627  {
628  lclAddIssue(m_rIssueCollection, SwResId(STR_HEADINGS_NOT_IN_ORDER));
629  }
630  m_nPreviousLevel = nLevel;
631  }
632  }
633 };
634 
635 class DocumentCheck : public BaseCheck
636 {
637 public:
638  DocumentCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
639  : BaseCheck(rIssueCollection)
640  {
641  }
642 
643  virtual void check(SwDoc* pDoc) = 0;
644 };
645 
646 // Check default language
647 class DocumentDefaultLanguageCheck : public DocumentCheck
648 {
649 public:
650  DocumentDefaultLanguageCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
651  : DocumentCheck(rIssueCollection)
652  {
653  }
654 
655  void check(SwDoc* pDoc) override
656  {
657  // TODO maybe - also check RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE if CJK or CTL are enabled
658  const SvxLanguageItem& rLang = pDoc->GetDefault(RES_CHRATR_LANGUAGE);
659  LanguageType eLanguage = rLang.GetLanguage();
660  if (eLanguage == LANGUAGE_NONE)
661  {
662  lclAddIssue(m_rIssueCollection, SwResId(STR_DOCUMENT_DEFAULT_LANGUAGE),
664  }
665  else
666  {
667  for (SwTextFormatColl* pTextFormatCollection : *pDoc->GetTextFormatColls())
668  {
669  const SwAttrSet& rAttrSet = pTextFormatCollection->GetAttrSet();
670  if (rAttrSet.GetLanguage(false).GetLanguage() == LANGUAGE_NONE)
671  {
672  OUString sName = pTextFormatCollection->GetName();
673  OUString sIssueText
674  = SwResId(STR_STYLE_NO_LANGUAGE).replaceAll("%STYLE_NAME%", sName);
675  lclAddIssue(m_rIssueCollection, sIssueText,
677  }
678  }
679  }
680  }
681 };
682 
683 class DocumentTitleCheck : public DocumentCheck
684 {
685 public:
686  DocumentTitleCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
687  : DocumentCheck(rIssueCollection)
688  {
689  }
690 
691  void check(SwDoc* pDoc) override
692  {
693  SwDocShell* pShell = pDoc->GetDocShell();
694  if (pShell)
695  {
696  const uno::Reference<document::XDocumentPropertiesSupplier> xDPS(pShell->GetModel(),
697  uno::UNO_QUERY_THROW);
698  const uno::Reference<document::XDocumentProperties> xDocumentProperties(
699  xDPS->getDocumentProperties());
700  OUString sTitle = xDocumentProperties->getTitle();
701  if (sTitle.isEmpty())
702  {
703  lclAddIssue(m_rIssueCollection, SwResId(STR_DOCUMENT_TITLE),
705  }
706  }
707  }
708 };
709 
710 class FootnoteEndnoteCheck : public DocumentCheck
711 {
712 public:
713  FootnoteEndnoteCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
714  : DocumentCheck(rIssueCollection)
715  {
716  }
717 
718  void check(SwDoc* pDoc) override
719  {
720  for (SwTextFootnote const* pTextFootnote : pDoc->GetFootnoteIdxs())
721  {
722  SwFormatFootnote const& rFootnote = pTextFootnote->GetFootnote();
723  if (rFootnote.IsEndNote())
724  {
725  lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_ENDNOTES));
726  }
727  else
728  {
729  lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_FOOTNOTES));
730  }
731  }
732  }
733 };
734 
735 } // end anonymous namespace
736 
737 // Check Shapes, TextBox
739 {
740  if (!pObject)
741  return;
742 
743  if (pObject->GetObjIdentifier() == OBJ_CUSTOMSHAPE || pObject->GetObjIdentifier() == OBJ_TEXT)
744  {
745  OUString sAlternative = pObject->GetTitle();
746  if (sAlternative.isEmpty())
747  {
748  OUString sName = pObject->GetName();
749  OUString sIssueText = SwResId(STR_NO_ALT).replaceAll("%OBJECT_NAME%", sName);
751  }
752  }
753 }
754 
756 {
757  if (m_pDoc == nullptr)
758  return;
759 
760  std::vector<std::unique_ptr<DocumentCheck>> aDocumentChecks;
761  aDocumentChecks.push_back(std::make_unique<DocumentDefaultLanguageCheck>(m_aIssueCollection));
762  aDocumentChecks.push_back(std::make_unique<DocumentTitleCheck>(m_aIssueCollection));
763  aDocumentChecks.push_back(std::make_unique<FootnoteEndnoteCheck>(m_aIssueCollection));
764 
765  for (std::unique_ptr<DocumentCheck>& rpDocumentCheck : aDocumentChecks)
766  {
767  rpDocumentCheck->check(m_pDoc);
768  }
769 
770  std::vector<std::unique_ptr<NodeCheck>> aNodeChecks;
771  aNodeChecks.push_back(std::make_unique<NoTextNodeAltTextCheck>(m_aIssueCollection));
772  aNodeChecks.push_back(std::make_unique<TableNodeMergeSplitCheck>(m_aIssueCollection));
773  aNodeChecks.push_back(std::make_unique<NumberingCheck>(m_aIssueCollection));
774  aNodeChecks.push_back(std::make_unique<HyperlinkCheck>(m_aIssueCollection));
775  aNodeChecks.push_back(std::make_unique<TextContrastCheck>(m_aIssueCollection));
776  aNodeChecks.push_back(std::make_unique<BlinkingTextCheck>(m_aIssueCollection));
777  aNodeChecks.push_back(std::make_unique<HeaderCheck>(m_aIssueCollection));
778  aNodeChecks.push_back(std::make_unique<TextFormattingCheck>(m_aIssueCollection));
779 
780  auto const& pNodes = m_pDoc->GetNodes();
781  SwNode* pNode = nullptr;
782  for (sal_uLong n = 0; n < pNodes.Count(); ++n)
783  {
784  pNode = pNodes[n];
785  if (pNode)
786  {
787  for (std::unique_ptr<NodeCheck>& rpNodeCheck : aNodeChecks)
788  {
789  rpNodeCheck->check(pNode);
790  }
791  }
792  }
793 
795  auto* pModel = rDrawModelAccess.GetDrawModel();
796  for (sal_uInt16 nPage = 0; nPage < pModel->GetPageCount(); ++nPage)
797  {
798  SdrPage* pPage = pModel->GetPage(nPage);
799  for (size_t nObject = 0; nObject < pPage->GetObjCount(); ++nObject)
800  {
801  SdrObject* pObject = pPage->GetObj(nObject);
802  if (pObject)
803  checkObject(pObject);
804  }
805  }
806 }
807 
808 } // end sw namespace
809 
810 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool IsTableComplex() const
Definition: swtable.cxx:1444
#define LANGUAGE_NONE
#define RES_CHRATR_WEIGHT
Definition: hintids.hxx:177
int m_nPreviousLevel
int GetAssignedOutlineStyleLevel() const
Definition: fmtcol.cxx:587
Represents the style of a paragraph.
Definition: fmtcol.hxx:55
OBJ_CUSTOMSHAPE
bool IsGrfNode() const
Definition: node.hxx:656
#define RES_CHRATR_FONTSIZE
Definition: hintids.hxx:170
#define RES_CHRATR_LANGUAGE
Definition: hintids.hxx:172
#define RES_CHRATR_RELIEF
Definition: hintids.hxx:198
const OUString & GetText() const
Definition: ndtxt.hxx:210
SwDocShell * GetDocShell()
Definition: doc.hxx:1348
#define RES_CHRATR_CJK_POSTURE
Definition: hintids.hxx:187
OBJ_TEXT
sal_uIntPtr sal_uLong
static css::uno::Reference< css::text::XTextContent > CreateXParagraph(SwDoc &rDoc, SwTextNode *pTextNode, css::uno::Reference< css::text::XText > const &xParentText=nullptr, const sal_Int32 nSelStart=-1, const sal_Int32 nSelEnd=-1)
const SwPageDesc * FindPageDesc(size_t *pPgDescNdIdx=nullptr) const
Search PageDesc with which this node is formatted.
Definition: node.cxx:464
const SvxLanguageItem & GetLanguage(bool=true) const
Definition: charatr.hxx:92
sal_Int64 n
SdrObject * GetObj(size_t nNum) const
Definition: doc.hxx:186
size_t GetObjCount() const
#define RES_CHRATR_CJK_WEIGHT
Definition: hintids.hxx:188
#define RES_CHRATR_FONT
Definition: hintids.hxx:169
const std::vector< std::pair< OUString, OUString > > m_aNumberingCombinations
SwTableLine is one table row in the document model.
Definition: swtable.hxx:344
const std::shared_ptr< SfxItemSet > & GetStyleHandle() const
Definition: fmtautofmt.hxx:49
bool IsEndNote() const
Definition: fmtftn.hxx:74
Dialog to specify the properties of date form field.
#define RES_CHRATR_CJK_FONT
Definition: hintids.hxx:184
void checkObject(SdrObject *pObject)
css::uno::Reference< css::frame::XModel > GetModel() const
#define RES_CHRATR_CJK_FONTSIZE
Definition: hintids.hxx:185
sal_uInt16 Which() const
Definition: txatbase.hxx:110
sal_Int32 GetAnyEnd() const
end (if available), else start
Definition: txatbase.hxx:153
EmbeddedObjectRef * pObject
SwTableFormat * GetFrameFormat()
Definition: swtable.hxx:201
SwTextNode * m_pPreviousTextNode
size_type size() const
Definition: swtable.hxx:74
#define XATTR_FILLCOLOR
IDocumentDrawModelAccess const & getIDocumentDrawModelAccess() const
Definition: doc.cxx:154
SwTableNode * GetTableNode()
Definition: node.hxx:599
SwNodeType GetNodeType() const
Definition: node.hxx:144
#define RES_CHRATR_CTL_FONTSIZE
Definition: hintids.hxx:190
const OUString & GetName() const
Definition: format.hxx:111
const SfxPoolItem & GetDefault(sal_uInt16 nFormatHint) const
Get the default attribute in this document.
Definition: docfmt.cxx:651
sal_Int32 GetStart() const
Definition: txatbase.hxx:82
const SwTable & GetTable() const
Definition: node.hxx:497
double getBlue() const
const char * sName
const Color & GetColorValue() const
sfx::AccessibilityIssueCollection & m_rIssueCollection
bool IsOLENode() const
Definition: node.hxx:652
#define RES_CHRATR_COLOR
Definition: hintids.hxx:165
double getRed() const
Style of a layout element.
Definition: frmfmt.hxx:57
#define RES_CHRATR_OVERLINE
Definition: hintids.hxx:200
size_t Count() const
Definition: ndhints.hxx:142
SwTextAttr * Get(size_t nPos) const
Definition: ndhints.hxx:144
#define RES_CHRATR_CTL_FONT
Definition: hintids.hxx:189
std::vector< std::shared_ptr< AccessibilityIssue > > & getIssues()
virtual sal_uInt16 GetObjIdentifier() const
int i
SwNoTextNode * GetNoTextNode()
Definition: ndnotxt.hxx:96
SwDoc * GetDoc()
Definition: node.hxx:702
virtual const SwDrawModel * GetDrawModel() const =0
Draw Model and id accessors.
OUString GetTitle() const
#define RES_CHRATR_UNDERLINE
Definition: hintids.hxx:176
SwpHints & GetSwpHints()
getters for SwpHints
Definition: ndtxt.hxx:808
OUString SwResId(const char *pId)
Definition: swmodule.cxx:178
SwTableLines & GetTabLines()
Definition: swtable.hxx:198
OUString GetName() const
SwTable is one table in the document model, containing rows (which contain cells).
Definition: swtable.hxx:110
#define RES_CHRATR_CONTOUR
Definition: hintids.hxx:166
#define RES_CHRATR_EMPHASIS_MARK
Definition: hintids.hxx:195
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > > check(dp_misc::DescriptionInfoset const &infoset)
SwFrameFormat & GetMaster()
Definition: pagedesc.hxx:217
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:79
LanguageType GetLanguage() const
#define RES_TXTATR_AUTOFMT
Definition: hintids.hxx:234
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:67
const SwTextFormatColls * GetTextFormatColls() const
Definition: doc.hxx:777
#define RES_CHRATR_POSTURE
Definition: hintids.hxx:173
double getGreen() const
#define RES_CHRATR_CTL_WEIGHT
Definition: hintids.hxx:193
#define RES_CHRATR_CTL_POSTURE
Definition: hintids.hxx:192
OUString GetTitle() const
Definition: ndnotxt.cxx:257
SwNodes & GetNodes()
Definition: doc.hxx:404
SwTableBox is one table cell in the document model.
Definition: swtable.hxx:386
SwTableNode is derived from SwStartNode.
AccessibilityIssueID
constexpr::Color COL_WHITE(0xFF, 0xFF, 0xFF)
basegfx::BColor getBColor() const
#define RES_CHRATR_SHADOWED
Definition: hintids.hxx:175
SwFootnoteIdxs & GetFootnoteIdxs()
Definition: doc.hxx:629
#define SAL_WARN(area, stream)
#define RES_CHRATR_CROSSEDOUT
Definition: hintids.hxx:167
const SwFormatAutoFormat & GetAutoFormat() const
Definition: txatbase.hxx:185
const SwAttrSet & GetAttrSet() const
For querying the attribute array.
Definition: format.hxx:116
const SfxPoolItem * GetItem(sal_uInt16 nWhich, bool bSearchInParent=true) const
bool IsTextNode() const
Definition: node.hxx:636
SwFrameFormat * GetFlyFormat() const
If node is in a fly return the respective format.
Definition: node.cxx:708
void remove_duplicates(std::vector< T > &rVector)
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:842
AccessibilityIssueCollection m_aIssueCollection
bool HasHints() const
Definition: ndtxt.hxx:220
Base class of the Writer document model elements.
Definition: node.hxx:79
SwTextFormatColl * GetTextColl() const
Definition: ndtxt.hxx:836