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 #include <svx/swframetypes.hxx>
36 #include <fmtanchr.hxx>
37 #include <dcontact.hxx>
38 
39 namespace sw
40 {
41 namespace
42 {
43 std::shared_ptr<sw::AccessibilityIssue>
44 lclAddIssue(sfx::AccessibilityIssueCollection& rIssueCollection, OUString const& rText,
46 {
47  auto pIssue = std::make_shared<sw::AccessibilityIssue>(eIssue);
48  pIssue->m_aIssueText = rText;
49  rIssueCollection.getIssues().push_back(pIssue);
50  return pIssue;
51 }
52 
53 class BaseCheck
54 {
55 protected:
57 
58 public:
59  BaseCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
60  : m_rIssueCollection(rIssueCollection)
61  {
62  }
63  virtual ~BaseCheck() {}
64 };
65 
66 class NodeCheck : public BaseCheck
67 {
68 public:
69  NodeCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
70  : BaseCheck(rIssueCollection)
71  {
72  }
73 
74  virtual void check(SwNode* pCurrent) = 0;
75 };
76 
77 // Check NoTextNodes: Graphic, OLE for alt (title) text
78 class NoTextNodeAltTextCheck : public NodeCheck
79 {
80  void checkNoTextNode(SwNoTextNode* pNoTextNode)
81  {
82  if (!pNoTextNode)
83  return;
84 
85  OUString sAlternative = pNoTextNode->GetTitle();
86  if (!sAlternative.isEmpty())
87  return;
88 
89  OUString sName = pNoTextNode->GetFlyFormat()->GetName();
90 
91  OUString sIssueText = SwResId(STR_NO_ALT).replaceAll("%OBJECT_NAME%", sName);
92 
93  if (pNoTextNode->IsOLENode())
94  {
95  auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
97  pIssue->setDoc(pNoTextNode->GetDoc());
98  pIssue->setIssueObject(IssueObject::OLE);
99  pIssue->setObjectID(pNoTextNode->GetFlyFormat()->GetName());
100  }
101  else if (pNoTextNode->IsGrfNode())
102  {
103  auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
105  pIssue->setDoc(pNoTextNode->GetDoc());
106  pIssue->setIssueObject(IssueObject::GRAPHIC);
107  pIssue->setObjectID(pNoTextNode->GetFlyFormat()->GetName());
108  }
109  }
110 
111 public:
112  NoTextNodeAltTextCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
113  : NodeCheck(rIssueCollection)
114  {
115  }
116 
117  void check(SwNode* pCurrent) override
118  {
119  if (pCurrent->GetNodeType() & SwNodeType::NoTextMask)
120  {
121  SwNoTextNode* pNoTextNode = pCurrent->GetNoTextNode();
122  if (pNoTextNode)
123  checkNoTextNode(pNoTextNode);
124  }
125  }
126 };
127 
128 // Check Table node if the table is merged and split.
129 class TableNodeMergeSplitCheck : public NodeCheck
130 {
131 private:
132  void addTableIssue(SwTable const& rTable, SwDoc* pDoc)
133  {
134  const SwTableFormat* pFormat = rTable.GetFrameFormat();
135  OUString sName = pFormat->GetName();
136  OUString sIssueText = SwResId(STR_TABLE_MERGE_SPLIT).replaceAll("%OBJECT_NAME%", sName);
137  auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
139  pIssue->setDoc(pDoc);
140  pIssue->setIssueObject(IssueObject::TABLE);
141  pIssue->setObjectID(sName);
142  }
143 
144  void checkTableNode(SwTableNode* pTableNode)
145  {
146  if (!pTableNode)
147  return;
148 
149  SwTable const& rTable = pTableNode->GetTable();
150  SwDoc* pDoc = pTableNode->GetDoc();
151  if (rTable.IsTableComplex())
152  {
153  addTableIssue(rTable, pDoc);
154  }
155  else
156  {
157  if (rTable.GetTabLines().size() > 1)
158  {
159  int i = 0;
160  size_t nFirstLineSize = 0;
161  bool bAllColumnsSameSize = true;
162  bool bCellSpansOverMoreRows = false;
163 
164  for (SwTableLine const* pTableLine : rTable.GetTabLines())
165  {
166  if (i == 0)
167  {
168  nFirstLineSize = pTableLine->GetTabBoxes().size();
169  }
170  else
171  {
172  size_t nLineSize = pTableLine->GetTabBoxes().size();
173  if (nFirstLineSize != nLineSize)
174  {
175  bAllColumnsSameSize = false;
176  }
177  }
178  i++;
179 
180  // Check for row span in each table box (cell)
181  for (SwTableBox const* pBox : pTableLine->GetTabBoxes())
182  {
183  if (pBox->getRowSpan() > 1)
184  bCellSpansOverMoreRows = true;
185  }
186  }
187  if (!bAllColumnsSameSize || bCellSpansOverMoreRows)
188  {
189  addTableIssue(rTable, pDoc);
190  }
191  }
192  }
193  }
194 
195 public:
196  TableNodeMergeSplitCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
197  : NodeCheck(rIssueCollection)
198  {
199  }
200 
201  void check(SwNode* pCurrent) override
202  {
203  if (pCurrent->GetNodeType() & SwNodeType::Table)
204  {
205  SwTableNode* pTableNode = pCurrent->GetTableNode();
206  if (pTableNode)
207  checkTableNode(pTableNode);
208  }
209  }
210 };
211 
212 class NumberingCheck : public NodeCheck
213 {
214 private:
216 
217  const std::vector<std::pair<OUString, OUString>> m_aNumberingCombinations{
218  { "1.", "2." }, { "(1)", "(2)" }, { "1)", "2)" }, { "a.", "b." }, { "(a)", "(b)" },
219  { "a)", "b)" }, { "A.", "B." }, { "(A)", "(B)" }, { "A)", "B)" }
220  };
221 
222 public:
223  NumberingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
224  : NodeCheck(rIssueCollection)
225  , m_pPreviousTextNode(nullptr)
226  {
227  }
228 
229  void check(SwNode* pCurrent) override
230  {
231  if (!pCurrent->IsTextNode())
232  return;
233 
234  if (m_pPreviousTextNode)
235  {
236  for (auto& rPair : m_aNumberingCombinations)
237  {
238  if (pCurrent->GetTextNode()->GetText().startsWith(rPair.second)
239  && m_pPreviousTextNode->GetText().startsWith(rPair.first))
240  {
241  OUString sNumbering = rPair.first + " " + rPair.second + "...";
242  OUString sIssueText
243  = SwResId(STR_FAKE_NUMBERING).replaceAll("%NUMBERING%", sNumbering);
244  lclAddIssue(m_rIssueCollection, sIssueText);
245  }
246  }
247  }
248  m_pPreviousTextNode = pCurrent->GetTextNode();
249  }
250 };
251 
252 class HyperlinkCheck : public NodeCheck
253 {
254 private:
255  void checkTextRange(uno::Reference<text::XTextRange> const& xTextRange)
256  {
257  uno::Reference<beans::XPropertySet> xProperties(xTextRange, uno::UNO_QUERY);
258  if (!xProperties->getPropertySetInfo()->hasPropertyByName("HyperLinkURL"))
259  return;
260 
261  OUString sHyperlink;
262  xProperties->getPropertyValue("HyperLinkURL") >>= sHyperlink;
263  if (!sHyperlink.isEmpty())
264  {
265  OUString sText = xTextRange->getString();
266  if (INetURLObject(sText) == INetURLObject(sHyperlink))
267  {
268  OUString sIssueText
269  = SwResId(STR_HYPERLINK_TEXT_IS_LINK).replaceFirst("%LINK%", sHyperlink);
270  lclAddIssue(m_rIssueCollection, sIssueText);
271  }
272  }
273  }
274 
275 public:
276  HyperlinkCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
277  : NodeCheck(rIssueCollection)
278  {
279  }
280 
281  void check(SwNode* pCurrent) override
282  {
283  if (!pCurrent->IsTextNode())
284  return;
285 
286  SwTextNode* pTextNode = pCurrent->GetTextNode();
287  uno::Reference<text::XTextContent> xParagraph
288  = SwXParagraph::CreateXParagraph(*pTextNode->GetDoc(), pTextNode);
289  if (!xParagraph.is())
290  return;
291 
292  uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph, uno::UNO_QUERY);
293  uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
294  while (xRunEnum->hasMoreElements())
295  {
296  uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
297  if (xRun.is())
298  {
299  checkTextRange(xRun);
300  }
301  }
302  }
303 };
304 
305 // Based on https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
306 double calculateRelativeLuminance(Color const& rColor)
307 {
308  // Convert to BColor which has R, G, B colors components
309  // represented by a floating point number from [0.0, 1.0]
310  const basegfx::BColor aBColor = rColor.getBColor();
311 
312  double r = aBColor.getRed();
313  double g = aBColor.getGreen();
314  double b = aBColor.getBlue();
315 
316  // Calculate the values according to the described algorithm
317  r = (r <= 0.03928) ? r / 12.92 : std::pow((r + 0.055) / 1.055, 2.4);
318  g = (g <= 0.03928) ? g / 12.92 : std::pow((g + 0.055) / 1.055, 2.4);
319  b = (b <= 0.03928) ? b / 12.92 : std::pow((b + 0.055) / 1.055, 2.4);
320 
321  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
322 }
323 
324 // TODO move to common color tools (BColorTools maybe)
325 // Based on https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio
326 double calculateContrastRatio(Color const& rColor1, Color const& rColor2)
327 {
328  const double fLuminance1 = calculateRelativeLuminance(rColor1);
329  const double fLuminance2 = calculateRelativeLuminance(rColor2);
330  const std::pair<const double, const double> aMinMax = std::minmax(fLuminance1, fLuminance2);
331 
332  // (L1 + 0.05) / (L2 + 0.05)
333  // L1 is the lighter color (greater luminance value)
334  // L2 is the darker color (smaller luminance value)
335  return (aMinMax.second + 0.05) / (aMinMax.first + 0.05);
336 }
337 
338 class TextContrastCheck : public NodeCheck
339 {
340 private:
341  void checkTextRange(uno::Reference<text::XTextRange> const& xTextRange,
342  uno::Reference<text::XTextContent> const& xParagraph, SwTextNode* pTextNode)
343  {
344  sal_Int32 nParaBackColor(COL_AUTO);
345  uno::Reference<beans::XPropertySet> xParagraphProperties(xParagraph, uno::UNO_QUERY);
346  if (!(xParagraphProperties->getPropertyValue("ParaBackColor") >>= nParaBackColor))
347  {
348  SAL_WARN("sw.a11y", "ParaBackColor void");
349  return;
350  }
351 
352  uno::Reference<beans::XPropertySet> xProperties(xTextRange, uno::UNO_QUERY);
353  if (!xProperties.is())
354  return;
355 
356  // Foreground color
357  sal_Int32 nCharColor = {}; // spurious -Werror=maybe-uninitialized
358  if (!(xProperties->getPropertyValue("CharColor") >>= nCharColor))
359  { // not sure this is impossible, can the default be void?
360  SAL_WARN("sw.a11y", "CharColor void");
361  return;
362  }
363  Color aForegroundColor(nCharColor);
364  if (aForegroundColor == COL_AUTO)
365  return;
366 
367  const SwPageDesc* pPageDescription = pTextNode->FindPageDesc();
368  const SwFrameFormat& rPageFormat = pPageDescription->GetMaster();
369  const SwAttrSet& rPageSet = rPageFormat.GetAttrSet();
370 
371  const XFillStyleItem* pXFillStyleItem(
372  rPageSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE, false));
373  Color aPageBackground(COL_AUTO);
374 
375  if (pXFillStyleItem && pXFillStyleItem->GetValue() == css::drawing::FillStyle_SOLID)
376  {
377  const XFillColorItem* rXFillColorItem
378  = rPageSet.GetItem<XFillColorItem>(XATTR_FILLCOLOR, false);
379  aPageBackground = rXFillColorItem->GetColorValue();
380  }
381 
382  sal_Int32 nCharBackColor(COL_AUTO);
383 
384  if (!(xProperties->getPropertyValue("CharBackColor") >>= nCharBackColor))
385  {
386  SAL_WARN("sw.a11y", "CharBackColor void");
387  return;
388  }
389  // Determine the background color
390  // Try Character background (highlight)
391  Color aBackgroundColor(nCharBackColor);
392 
393  // If not character background color, try paragraph background color
394  if (aBackgroundColor == COL_AUTO)
395  aBackgroundColor = Color(nParaBackColor);
396 
397  // If not paragraph background color, try page color
398  if (aBackgroundColor == COL_AUTO)
399  aBackgroundColor = aPageBackground;
400 
401  // If not page color, assume white background color
402  if (aBackgroundColor == COL_AUTO)
403  aBackgroundColor = COL_WHITE;
404 
405  double fContrastRatio = calculateContrastRatio(aForegroundColor, aBackgroundColor);
406  if (fContrastRatio < 4.5)
407  {
408  lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_CONTRAST));
409  }
410  }
411 
412 public:
413  TextContrastCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
414  : NodeCheck(rIssueCollection)
415  {
416  }
417 
418  void check(SwNode* pCurrent) override
419  {
420  if (!pCurrent->IsTextNode())
421  return;
422 
423  SwTextNode* pTextNode = pCurrent->GetTextNode();
424  uno::Reference<text::XTextContent> xParagraph;
425  xParagraph = SwXParagraph::CreateXParagraph(*pTextNode->GetDoc(), pTextNode);
426  if (!xParagraph.is())
427  return;
428 
429  uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph, uno::UNO_QUERY);
430  uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
431  while (xRunEnum->hasMoreElements())
432  {
433  uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
434  if (xRun.is())
435  checkTextRange(xRun, xParagraph, pTextNode);
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  return;
523 
524  o3tl::remove_duplicates(aFormattings);
525  auto pIssue = 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  void check(SwNode* pCurrent) override
535  {
536  if (!pCurrent->IsTextNode())
537  return;
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 class BlinkingTextCheck : public NodeCheck
556 {
557 private:
558  void checkTextRange(uno::Reference<text::XTextRange> const& xTextRange)
559  {
560  uno::Reference<beans::XPropertySet> xProperties(xTextRange, uno::UNO_QUERY);
561  if (xProperties.is() && xProperties->getPropertySetInfo()->hasPropertyByName("CharFlash"))
562  {
563  bool bBlinking = false;
564  xProperties->getPropertyValue("CharFlash") >>= bBlinking;
565 
566  if (bBlinking)
567  {
568  lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_BLINKING));
569  }
570  }
571  }
572 
573 public:
574  BlinkingTextCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
575  : NodeCheck(rIssueCollection)
576  {
577  }
578 
579  void check(SwNode* pCurrent) override
580  {
581  if (!pCurrent->IsTextNode())
582  return;
583 
584  SwTextNode* pTextNode = pCurrent->GetTextNode();
585  uno::Reference<text::XTextContent> xParagraph;
586  xParagraph = SwXParagraph::CreateXParagraph(*pTextNode->GetDoc(), pTextNode);
587  if (!xParagraph.is())
588  return;
589 
590  uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph, uno::UNO_QUERY);
591  uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
592  while (xRunEnum->hasMoreElements())
593  {
594  uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
595  if (xRun.is())
596  checkTextRange(xRun);
597  }
598  }
599 };
600 
601 class HeaderCheck : public NodeCheck
602 {
603 private:
605 
606 public:
607  HeaderCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
608  : NodeCheck(rIssueCollection)
609  , m_nPreviousLevel(0)
610  {
611  }
612 
613  void check(SwNode* pCurrent) override
614  {
615  if (!pCurrent->IsTextNode())
616  return;
617 
618  SwTextNode* pTextNode = pCurrent->GetTextNode();
619  SwTextFormatColl* pCollection = pTextNode->GetTextColl();
620  int nLevel = pCollection->GetAssignedOutlineStyleLevel();
621  if (nLevel < 0)
622  return;
623 
624  if (nLevel > m_nPreviousLevel && std::abs(nLevel - m_nPreviousLevel) > 1)
625  {
626  lclAddIssue(m_rIssueCollection, SwResId(STR_HEADINGS_NOT_IN_ORDER));
627  }
628  m_nPreviousLevel = nLevel;
629  }
630 };
631 
632 // ISO 142891-1 : 7.14
633 class NonInteractiveFormCheck : public NodeCheck
634 {
635 public:
636  NonInteractiveFormCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
637  : NodeCheck(rIssueCollection)
638  {
639  }
640 
641  void check(SwNode* pCurrent) override
642  {
643  if (!pCurrent->IsTextNode())
644  return;
645 
646  const auto& text = pCurrent->GetTextNode()->GetText();
647 
648  // Series of tests to detect if there are fake forms in the text.
649 
650  bool bCheck = text.indexOf("___") == -1; // Repeated underscores.
651 
652  if (bCheck)
653  bCheck = text.indexOf("....") == -1; // Repeated dots.
654 
655  if (bCheck)
656  bCheck = text.indexOf(u"……") == -1; // Repeated ellipsis.
657 
658  if (bCheck)
659  bCheck = text.indexOf(u"….") == -1; // A dot after an ellipsis.
660 
661  if (bCheck)
662  bCheck = text.indexOf(u".…") == -1; // An ellipsis after a dot.
663 
664  // Checking if all the tests are passed successfully. If not, adding a warning.
665  if (!bCheck)
666  lclAddIssue(m_rIssueCollection, SwResId(STR_NON_INTERACTIVE_FORMS));
667  }
668 };
669 
671 class FloatingTextCheck : public NodeCheck
672 {
673 public:
674  FloatingTextCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
675  : NodeCheck(rIssueCollection)
676  {
677  }
678 
679  void check(SwNode* pCurrent) override
680  {
681  // if node is a text-node and if it has text, we proceed. Otherwise - return.
682  const SwTextNode* textNode = pCurrent->GetTextNode();
683  if (!textNode || textNode->GetText().isEmpty())
684  return;
685 
686  // If a node is in fly and if it is not anchored as char, throw warning.
687  const SwNode* startFly = pCurrent->FindFlyStartNode();
688  if (startFly
689  && startFly->GetFlyFormat()->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
690  lclAddIssue(m_rIssueCollection, SwResId(STR_FLOATING_TEXT));
691  }
692 };
693 
694 class DocumentCheck : public BaseCheck
695 {
696 public:
697  DocumentCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
698  : BaseCheck(rIssueCollection)
699  {
700  }
701 
702  virtual void check(SwDoc* pDoc) = 0;
703 };
704 
705 // Check default language
706 class DocumentDefaultLanguageCheck : public DocumentCheck
707 {
708 public:
709  DocumentDefaultLanguageCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
710  : DocumentCheck(rIssueCollection)
711  {
712  }
713 
714  void check(SwDoc* pDoc) override
715  {
716  // TODO maybe - also check RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE if CJK or CTL are enabled
717  const SvxLanguageItem& rLang = pDoc->GetDefault(RES_CHRATR_LANGUAGE);
718  LanguageType eLanguage = rLang.GetLanguage();
719  if (eLanguage == LANGUAGE_NONE)
720  {
721  lclAddIssue(m_rIssueCollection, SwResId(STR_DOCUMENT_DEFAULT_LANGUAGE),
723  }
724  else
725  {
726  for (SwTextFormatColl* pTextFormatCollection : *pDoc->GetTextFormatColls())
727  {
728  const SwAttrSet& rAttrSet = pTextFormatCollection->GetAttrSet();
729  if (rAttrSet.GetLanguage(false).GetLanguage() == LANGUAGE_NONE)
730  {
731  OUString sName = pTextFormatCollection->GetName();
732  OUString sIssueText
733  = SwResId(STR_STYLE_NO_LANGUAGE).replaceAll("%STYLE_NAME%", sName);
734  lclAddIssue(m_rIssueCollection, sIssueText,
736  }
737  }
738  }
739  }
740 };
741 
742 class DocumentTitleCheck : public DocumentCheck
743 {
744 public:
745  DocumentTitleCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
746  : DocumentCheck(rIssueCollection)
747  {
748  }
749 
750  void check(SwDoc* pDoc) override
751  {
752  SwDocShell* pShell = pDoc->GetDocShell();
753  if (!pShell)
754  return;
755 
756  const uno::Reference<document::XDocumentPropertiesSupplier> xDPS(pShell->GetModel(),
757  uno::UNO_QUERY_THROW);
758  const uno::Reference<document::XDocumentProperties> xDocumentProperties(
759  xDPS->getDocumentProperties());
760  OUString sTitle = xDocumentProperties->getTitle();
761  if (sTitle.isEmpty())
762  {
763  lclAddIssue(m_rIssueCollection, SwResId(STR_DOCUMENT_TITLE),
765  }
766  }
767 };
768 
769 class FootnoteEndnoteCheck : public DocumentCheck
770 {
771 public:
772  FootnoteEndnoteCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
773  : DocumentCheck(rIssueCollection)
774  {
775  }
776 
777  void check(SwDoc* pDoc) override
778  {
779  for (SwTextFootnote const* pTextFootnote : pDoc->GetFootnoteIdxs())
780  {
781  SwFormatFootnote const& rFootnote = pTextFootnote->GetFootnote();
782  if (rFootnote.IsEndNote())
783  {
784  lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_ENDNOTES));
785  }
786  else
787  {
788  lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_FOOTNOTES));
789  }
790  }
791  }
792 };
793 
794 } // end anonymous namespace
795 
796 // Check Shapes, TextBox
798 {
799  if (!pObject)
800  return;
801 
802  // Checking if there is floating Writer text draw object and if so, throwing a warning.
803  // (Floating objects with text create problems with reading order)
804  if (pObject->HasText()
805  && FindFrameFormat(pObject)->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
806  lclAddIssue(m_aIssueCollection, SwResId(STR_FLOATING_TEXT));
807 
808  if (pObject->GetObjIdentifier() == OBJ_CUSTOMSHAPE || pObject->GetObjIdentifier() == OBJ_TEXT)
809  {
810  OUString sAlternative = pObject->GetTitle();
811  if (sAlternative.isEmpty())
812  {
813  OUString sName = pObject->GetName();
814  OUString sIssueText = SwResId(STR_NO_ALT).replaceAll("%OBJECT_NAME%", sName);
816  }
817  }
818 }
819 
821 {
822  if (m_pDoc == nullptr)
823  return;
824 
825  std::vector<std::unique_ptr<DocumentCheck>> aDocumentChecks;
826  aDocumentChecks.push_back(std::make_unique<DocumentDefaultLanguageCheck>(m_aIssueCollection));
827  aDocumentChecks.push_back(std::make_unique<DocumentTitleCheck>(m_aIssueCollection));
828  aDocumentChecks.push_back(std::make_unique<FootnoteEndnoteCheck>(m_aIssueCollection));
829 
830  for (std::unique_ptr<DocumentCheck>& rpDocumentCheck : aDocumentChecks)
831  {
832  rpDocumentCheck->check(m_pDoc);
833  }
834 
835  std::vector<std::unique_ptr<NodeCheck>> aNodeChecks;
836  aNodeChecks.push_back(std::make_unique<NoTextNodeAltTextCheck>(m_aIssueCollection));
837  aNodeChecks.push_back(std::make_unique<TableNodeMergeSplitCheck>(m_aIssueCollection));
838  aNodeChecks.push_back(std::make_unique<NumberingCheck>(m_aIssueCollection));
839  aNodeChecks.push_back(std::make_unique<HyperlinkCheck>(m_aIssueCollection));
840  aNodeChecks.push_back(std::make_unique<TextContrastCheck>(m_aIssueCollection));
841  aNodeChecks.push_back(std::make_unique<BlinkingTextCheck>(m_aIssueCollection));
842  aNodeChecks.push_back(std::make_unique<HeaderCheck>(m_aIssueCollection));
843  aNodeChecks.push_back(std::make_unique<TextFormattingCheck>(m_aIssueCollection));
844  aNodeChecks.push_back(std::make_unique<NonInteractiveFormCheck>(m_aIssueCollection));
845  aNodeChecks.push_back(std::make_unique<FloatingTextCheck>(m_aIssueCollection));
846 
847  auto const& pNodes = m_pDoc->GetNodes();
848  SwNode* pNode = nullptr;
849  for (sal_uLong n = 0; n < pNodes.Count(); ++n)
850  {
851  pNode = pNodes[n];
852  if (pNode)
853  {
854  for (std::unique_ptr<NodeCheck>& rpNodeCheck : aNodeChecks)
855  {
856  rpNodeCheck->check(pNode);
857  }
858  }
859  }
860 
862  auto* pModel = rDrawModelAccess.GetDrawModel();
863  for (sal_uInt16 nPage = 0; nPage < pModel->GetPageCount(); ++nPage)
864  {
865  SdrPage* pPage = pModel->GetPage(nPage);
866  for (size_t nObject = 0; nObject < pPage->GetObjCount(); ++nObject)
867  {
868  SdrObject* pObject = pPage->GetObj(nObject);
869  if (pObject)
870  checkObject(pObject);
871  }
872  }
873 }
874 
875 } // end sw namespace
876 
877 /* 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
virtual bool HasText() const
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:155
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
SwFrameFormat * FindFrameFormat(SdrObject *pObj)
The Get reverse way: seeks the format to the specified object.
Definition: dcontact.cxx:120
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
exports com.sun.star. text
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
const SwFormatAnchor & GetAnchor(bool=true) const
Definition: fmtanchr.hxx:81
int i
std::vector< std::shared_ptr< AccessibilityIssue > > & getIssues()
virtual sal_uInt16 GetObjIdentifier() const
RndStdIds GetAnchorId() const
Definition: fmtanchr.hxx:65
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:810
OUString SwResId(const char *pId)
Definition: swmodule.cxx:165
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
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_WHITE
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
basegfx::BColor getBColor() const
const SwStartNode * FindFlyStartNode() const
Definition: node.hxx:198
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:844
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:838
constexpr TypedWhichId< SvxContourItem > RES_CHRATR_CONTOUR(4)