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