/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. *
*/
auto nIndex = pCurrent->GetIndex(); auto nCount = pCurrent->GetNodes().Count();
nIndex++; // go to next node
while (pTextNode == nullptr && nIndex < nCount)
{ auto pNode = pCurrent->GetNodes()[nIndex]; if (pNode->IsTextNode())
pTextNode = pNode->GetTextNode();
nIndex++;
}
return pTextNode;
}
void lcl_SetHiddenIssues(const std::shared_ptr<sw::AccessibilityIssue>& pIssue)
{ switch (pIssue->m_eIssueID)
{ case sfx::AccessibilityIssueID::DOCUMENT_TITLE:
{ if (!officecfg::Office::Common::AccessibilityIssues::DocumentTitle::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::DOCUMENT_LANGUAGE:
{ if (!officecfg::Office::Common::AccessibilityIssues::DocumentLanguage::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::DOCUMENT_BACKGROUND:
{ if (!officecfg::Office::Common::AccessibilityIssues::DocumentBackground::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::STYLE_LANGUAGE:
{ if (!officecfg::Office::Common::AccessibilityIssues::DocumentStyleLanguage::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::LINKED_GRAPHIC:
{ if (!officecfg::Office::Common::AccessibilityIssues::LinkedGraphic::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::NO_ALT_OLE:
{ if (!officecfg::Office::Common::AccessibilityIssues::NoAltOleObj::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::NO_ALT_GRAPHIC:
{ if (!officecfg::Office::Common::AccessibilityIssues::NoAltGraphicObj::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::NO_ALT_SHAPE:
{ if (!officecfg::Office::Common::AccessibilityIssues::NoAltShapeObj::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::TABLE_MERGE_SPLIT:
{ if (!officecfg::Office::Common::AccessibilityIssues::TableMergeSplit::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::TEXT_NEW_LINES:
{ if (!officecfg::Office::Common::AccessibilityIssues::TextNewLines::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::TEXT_SPACES:
{ if (!officecfg::Office::Common::AccessibilityIssues::TextSpaces::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::TEXT_TABS:
{ if (!officecfg::Office::Common::AccessibilityIssues::TextTabs::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::TEXT_EMPTY_NUM_PARA:
{ if (!officecfg::Office::Common::AccessibilityIssues::TextEmptyNums::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::TABLE_FORMATTING:
{ if (!officecfg::Office::Common::AccessibilityIssues::TableFormattings::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::DIRECT_FORMATTING:
{ if (!officecfg::Office::Common::AccessibilityIssues::DirectFormattings::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::HYPERLINK_IS_TEXT:
{ if (!officecfg::Office::Common::AccessibilityIssues::HyperlinkText::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::HYPERLINK_SHORT:
{ if (!officecfg::Office::Common::AccessibilityIssues::HyperlinkShort::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::HYPERLINK_NO_NAME:
{ if (!officecfg::Office::Common::AccessibilityIssues::HyperlinkNoName::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER:
{ if (!officecfg::Office::Common::AccessibilityIssues::LinkInHeaderOrFooter::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::FAKE_FOOTNOTE:
{ if (!officecfg::Office::Common::AccessibilityIssues::FakeFootnotes::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::FAKE_CAPTION:
{ if (!officecfg::Office::Common::AccessibilityIssues::FakeCaptions::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::MANUAL_NUMBERING:
{ if (!officecfg::Office::Common::AccessibilityIssues::ManualNumbering::get())
pIssue->setHidden(true);
} break;
case sfx::AccessibilityIssueID::TEXT_CONTRAST:
{ if (!officecfg::Office::Common::AccessibilityIssues::TextContrast::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::TEXT_BLINKING:
{ if (!officecfg::Office::Common::AccessibilityIssues::TextBlinking::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::HEADINGS_NOT_IN_ORDER:
{ if (!officecfg::Office::Common::AccessibilityIssues::HeadingNotInOrder::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::NON_INTERACTIVE_FORMS:
{ if (!officecfg::Office::Common::AccessibilityIssues::NonInteractiveForms::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::FLOATING_TEXT:
{ if (!officecfg::Office::Common::AccessibilityIssues::Floatingtext::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::HEADING_IN_TABLE:
{ if (!officecfg::Office::Common::AccessibilityIssues::HeadingTable::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::HEADING_START:
{ if (!officecfg::Office::Common::AccessibilityIssues::HeadingStart::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::HEADING_ORDER:
{ if (!officecfg::Office::Common::AccessibilityIssues::HeadingOrder::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::CONTENT_CONTROL:
{ if (!officecfg::Office::Common::AccessibilityIssues::ContentControl::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::AVOID_FOOTNOTES:
{ if (!officecfg::Office::Common::AccessibilityIssues::AvoidFootnotes::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::AVOID_ENDNOTES:
{ if (!officecfg::Office::Common::AccessibilityIssues::AvoidEndnotes::get())
pIssue->setHidden(true);
} break; case sfx::AccessibilityIssueID::FONTWORKS:
{ if (!officecfg::Office::Common::AccessibilityIssues::FontWorks::get())
pIssue->setHidden(true);
} break; default:
{
SAL_WARN("sw.a11y", "Invalid issue ID."); break;
}
}
}
class NodeCheck : public BaseCheck
{ public:
NodeCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
: BaseCheck(rIssueCollection)
{
}
virtualvoid check(SwNode* pCurrent) = 0;
};
// Check NoTextNodes: Graphic, OLE for alt (title) text class NoTextNodeAltTextCheck : public NodeCheck
{ void checkNoTextNode(SwNoTextNode* pNoTextNode)
{ if (!pNoTextNode) return;
const SwFrameFormat* pFrameFormat = pNoTextNode->GetFlyFormat(); if (!pFrameFormat) return;
// linked graphic with broken link if (pNoTextNode->IsGrfNode() && pNoTextNode->GetGrfNode()->IsLinkedFile())
{
OUString sURL(pNoTextNode->GetGrfNode()->GetGraphic().getOriginURL()); if (!FStatHelper::IsDocument(sURL))
{
INetURLObject aURL(sURL);
OUString aSystemPath = sURL;
void check(SwNode* pCurrent) override
{ if (pCurrent->GetNodeType() & SwNodeType::Table)
{
SwTableNode* pTableNode = pCurrent->GetTableNode(); if (pTableNode)
checkTableNode(pTableNode);
}
}
};
class TableFormattingCheck : public NodeCheck
{ private: void checkTableNode(SwTableNode* pTableNode)
{ if (!pTableNode) return;
const SwTable& rTable = pTableNode->GetTable(); if (!rTable.IsTableComplex())
{
size_t nEmptyBoxes = 0;
size_t nBoxCount = 0; for (const SwTableLine* pTableLine : rTable.GetTabLines())
{
nBoxCount += pTableLine->GetTabBoxes().size(); for (const SwTableBox* pBox : pTableLine->GetTabBoxes()) if (pBox->IsEmpty())
++nEmptyBoxes;
} // If more than half of the boxes are empty we can assume that it is used for formatting if (nEmptyBoxes > nBoxCount / 2)
{ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_TABLE_FORMATTING),
sfx::AccessibilityIssueID::TABLE_FORMATTING,
sfx::AccessibilityIssueLevel::WARNLEV);
// check Hyperlinks in Header/Footer --> annotation is not nested if (rDocument.IsInHeaderFooter(*pTextNode))
{
pIssue
= lclAddIssue(m_rIssueCollection, SwResId(STR_LINK_TEXT_IS_NOT_NESTED),
sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER,
sfx::AccessibilityIssueLevel::WARNLEV);
// check other Link's in Header/Footer --> annotation is not nested if (pTextAttr->Which() == RES_TXTATR_FIELD && rDocument.IsInHeaderFooter(*pTextNode))
{ bool bWarning = false; const SwField* pField = pTextAttr->GetFormatField().GetField(); if (SwFieldIds::GetRef == pField->Which())
{
bWarning = true;
} elseif (SwFieldIds::TableOfAuthorities == pField->Which())
{ constauto& rAuthorityField = *static_cast<const SwAuthorityField*>(pField); if (auto targetType = rAuthorityField.GetTargetType();
targetType == SwAuthorityField::TargetType::None)
{
bWarning = false;
} else
{
bWarning = true;
}
}
if (bWarning)
{ auto pIssue
= lclAddIssue(m_rIssueCollection, SwResId(STR_LINK_TEXT_IS_NOT_NESTED),
sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER,
sfx::AccessibilityIssueLevel::WARNLEV);
// Based on https://www.w3.org/TR/WCAG21/#dfn-relative-luminance double calculateRelativeLuminance(Color const& rColor)
{ // Convert to BColor which has R, G, B colors components // represented by a floating point number from [0.0, 1.0] const basegfx::BColor aBColor = rColor.getBColor();
double r = aBColor.getRed(); double g = aBColor.getGreen(); double b = aBColor.getBlue();
// Calculate the values according to the described algorithm
r = (r <= 0.04045) ? r / 12.92 : std::pow((r + 0.055) / 1.055, 2.4);
g = (g <= 0.04045) ? g / 12.92 : std::pow((g + 0.055) / 1.055, 2.4);
b = (b <= 0.04045) ? b / 12.92 : std::pow((b + 0.055) / 1.055, 2.4);
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
// TODO move to common color tools (BColorTools maybe) // Based on https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio double calculateContrastRatio(Color const& rColor1, Color const& rColor2)
{ constdouble fLuminance1 = calculateRelativeLuminance(rColor1); constdouble fLuminance2 = calculateRelativeLuminance(rColor2); const std::pair<constdouble, constdouble> aMinMax = std::minmax(fLuminance1, fLuminance2);
// (L1 + 0.05) / (L2 + 0.05) // L1 is the lighter color (greater luminance value) // L2 is the darker color (smaller luminance value) return (aMinMax.second + 0.05) / (aMinMax.first + 0.05);
}
// Determine required minimum contrast ratio for text with the given properties // according to https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html // * 3.0 for large text (font size >= 18 or bold and font size >= 14) // * 4.5 otherwise double minimumContrastRatio(const uno::Reference<beans::XPropertySet>& xProperties)
{ double fMinimumContrastRatio = 4.5; double fFontSize = 0; if (xProperties->getPropertyValue(u"CharHeight"_ustr) >>= fFontSize)
{ if (fFontSize >= 18)
fMinimumContrastRatio = 3.0; elseif (fFontSize >= 14)
{ double fCharWeight = 0; if (xProperties->getPropertyValue(u"CharWeight"_ustr) >>= fCharWeight)
{ if (fCharWeight == css::awt::FontWeight::BOLD
|| fCharWeight == css::awt::FontWeight::ULTRABOLD)
fMinimumContrastRatio = 3.0;
}
}
} return fMinimumContrastRatio;
}
class TextContrastCheck : public NodeCheck
{ private: void checkTextRange(uno::Reference<text::XTextRange> const& xTextRange,
uno::Reference<text::XTextContent> const& xParagraph, SwTextNode* pTextNode,
sal_Int32 nTextStart)
{ if (xTextRange->getString().isEmpty()) return;
Color nParaBackColor(COL_AUTO);
uno::Reference<beans::XPropertySet> xParagraphProperties(xParagraph, uno::UNO_QUERY); if (!(xParagraphProperties->getPropertyValue(u"ParaBackColor"_ustr) >>= nParaBackColor))
{
SAL_WARN("sw.a11y", "ParaBackColor void"); return;
}
uno::Reference<beans::XPropertySet> xProperties(xTextRange, uno::UNO_QUERY); if (!xProperties.is()) return;
// Foreground color
sal_Int32 nCharColor = {}; // spurious -Werror=maybe-uninitialized if (!(xProperties->getPropertyValue(u"CharColor"_ustr) >>= nCharColor))
{ // not sure this is impossible, can the default be void?
SAL_WARN("sw.a11y", "CharColor void"); return;
}
if (!(xProperties->getPropertyValue(u"CharBackColor"_ustr) >>= nCharBackColor))
{
SAL_WARN("sw.a11y", "CharBackColor void"); return;
} // Determine the background color // Try Character background (Character highlighting color)
Color aBackgroundColor(nCharBackColor);
// If not character background color, try paragraph background color if (aBackgroundColor == COL_AUTO)
aBackgroundColor = nParaBackColor; else
{
SwDocShell* pDocShell = pTextNode->GetDoc().GetDocShell(); if (!pDocShell) return;
case RES_CHRATR_COLOR:
{ const SvxColorItem* pStyleItem = nullptr;
if (pCharformat)
pStyleItem = pCharformat->GetItemIfSet(RES_CHRATR_COLOR, false);
if (!pStyleItem && pTextNode->GetTextColl())
{
pStyleItem
= pTextNode->GetTextColl()->GetItemIfSet(RES_CHRATR_COLOR, false);
}
if (!pStyleItem)
{
pStyleItem = &pTextNode->GetDoc().GetAttrPool().GetUserOrPoolDefaultItem(
RES_CHRATR_COLOR);
}
if (!SfxPoolItem::areSame(static_cast<const SvxColorItem*>(pItem), pStyleItem))
sFormattingType = "Font Color";
} break;
case RES_CHRATR_FONTSIZE:
{ // case RES_CHRATR_CJK_FONTSIZE: // case RES_CHRATR_CTL_FONTSIZE: // TODO: check depending on which lang is used Western, Complex, Asia const SvxFontHeightItem* pStyleItem = nullptr;
if (pCharformat)
{
pStyleItem = pCharformat->GetItemIfSet(
TypedWhichId<SvxFontHeightItem>(pItem->Which()), false);
}
if (pTextNode->GetTextColl())
pStyleItem = pTextNode->GetTextColl()->GetItemIfSet(RES_CHRATR_COLOR, false);
if (!pStyleItem)
{
pStyleItem
= &pTextNode->GetDoc().GetAttrPool().GetUserOrPoolDefaultItem(RES_CHRATR_COLOR);
}
if (!SfxPoolItem::areSame(static_cast<const SvxColorItem*>(pItem), pStyleItem)) returntrue; else
pItem = nullptr;
}
// TODO: check depending on which lang is used Western, Complex, Asia if ((pItem = rSwAttrSet.GetItem(RES_CHRATR_FONTSIZE, false)) /*|| (pItem = rSwAttrSet.GetItem(RES_CHRATR_CJK_FONTSIZE, false))
|| (pItem = rSwAttrSet.GetItem(RES_CHRATR_CTL_FONTSIZE, false))*/
{ const SvxFontHeightItem* pStyleItem = nullptr;
if (pTextNode->GetTextColl())
{
pStyleItem = pTextNode->GetTextColl()->GetItemIfSet(
TypedWhichId<SvxFontHeightItem>(pItem->Which()), false);
}
if (!pStyleItem)
{
pStyleItem = &pTextNode->GetDoc().GetAttrPool().GetUserOrPoolDefaultItem(
TypedWhichId<SvxFontHeightItem>(pItem->Which()));
}
if (!SfxPoolItem::areSame(static_cast<const SvxFontHeightItem*>(pItem), pStyleItem)) returntrue; else
pItem = nullptr;
}
SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); if (!pWrtShell) return;
auto nParagraphLength = pTextNode->GetText().getLength(); if (nParagraphLength == 0)
{
SwTextNode* pPrevTextNode = getPrevTextNode(pCurrent); if (!pPrevTextNode) return;
if (pPrevTextNode->getLayoutFrame(pWrtShell->GetLayout()))
{ if (pPrevTextNode->GetText().getLength() == 0)
{ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_NEWLINES_SPACE),
sfx::AccessibilityIssueID::TEXT_NEW_LINES,
sfx::AccessibilityIssueLevel::WARNLEV);
pIssue->setIssueObject(IssueObject::TEXT);
pIssue->setNode(pTextNode);
pIssue->setDoc(rDocument);
}
}
} else
{ if (pTextNode->getLayoutFrame(pWrtShell->GetLayout()))
{ // Check for excess lines inside this paragraph const OUString& sParagraphText = pTextNode->GetText(); int nLineCount = 0; for (sal_Int32 i = 0; i < nParagraphLength; i++)
{ auto aChar = sParagraphText[i]; if (aChar == '\n')
{
nLineCount++; // Looking for 2 newline characters and above as one can be part of the line // break after a sentence if (nLineCount > 2)
{ auto pIssue
= lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_NEWLINES_SPACE),
sfx::AccessibilityIssueID::TEXT_NEW_LINES,
sfx::AccessibilityIssueLevel::WARNLEV);
pIssue->setIssueObject(IssueObject::TEXT);
pIssue->setNode(pTextNode);
pIssue->setDoc(rDocument);
pIssue->setStart(i);
pIssue->setEnd(i);
}
} // Don't count carriage return as normal character elseif (aChar != '\r')
{
nLineCount = 0;
}
}
}
}
}
};
class SpaceSpacingCheck : public NodeCheck
{ public:
SpaceSpacingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
: NodeCheck(rIssueCollection)
{
} void check(SwNode* pCurrent) override
{ if (!pCurrent->IsTextNode()) return;
SwTextNode* pTextNode = pCurrent->GetTextNode(); auto nParagraphLength = pTextNode->GetText().getLength(); const OUString& sParagraphText = pTextNode->GetText();
sal_Int32 nSpaceCount = 0;
sal_Int32 nSpaceStart = 0;
sal_Int32 nTabCount = 0; bool bNonSpaceFound = false; bool bPreviousWasChar = false; bool bPreviousWasTab = false; for (sal_Int32 i = 0; i < nParagraphLength; i++)
{ switch (sParagraphText[i])
{ case' ':
{ if (bNonSpaceFound)
{
nSpaceCount++; if (nSpaceCount == 2)
nSpaceStart = i;
} break;
} case'\t':
{ // Don't warn about tabs in ToC auto pSection = SwDoc::GetCurrSection(SwPosition(*pTextNode, 0)); if (pSection && pSection->GetTOXBase()) continue;
// text between tabs or text align at least with two tabs if (bPreviousWasChar || bPreviousWasTab)
{
++nTabCount; if (bPreviousWasChar)
{
bPreviousWasChar = false;
bPreviousWasTab = true;
} if (nTabCount == 2)
{ auto pIssue = lclAddIssue(m_rIssueCollection,
SwResId(STR_AVOID_TABS_FORMATTING),
sfx::AccessibilityIssueID::TEXT_TABS,
sfx::AccessibilityIssueLevel::WARNLEV);
pIssue->setIssueObject(IssueObject::TEXT);
pIssue->setNode(pTextNode);
SwDoc& rDocument = pTextNode->GetDoc();
pIssue->setDoc(rDocument);
pIssue->setStart(0);
pIssue->setEnd(nParagraphLength);
}
} break;
} default:
{ if (nSpaceCount >= 2)
{ auto pIssue
= lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_SPACES_SPACE),
sfx::AccessibilityIssueID::TEXT_SPACES,
sfx::AccessibilityIssueLevel::WARNLEV);
pIssue->setIssueObject(IssueObject::TEXT);
pIssue->setNode(pTextNode);
SwDoc& rDocument = pTextNode->GetDoc();
pIssue->setDoc(rDocument);
pIssue->setStart(nSpaceStart);
pIssue->setEnd(nSpaceStart + nSpaceCount - 1);
}
bNonSpaceFound = true;
bPreviousWasChar = true;
bPreviousWasTab = false;
nSpaceCount = 0; break;
}
}
}
}
};
// Check if it's a real caption if (const SwNode* pStartFly = pCurrent->FindFlyStartNode())
{ const SwFrameFormat* pFormat = pStartFly->GetFlyFormat(); if (pFormat) return;
}
auto aIter = SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti>(*pTextNode); auto nCount = 0; for (auto aTextFrame = aIter.First(); aTextFrame; aTextFrame = aIter.Next())
{ auto aObjects = aTextFrame->GetDrawObjs(); if (aObjects)
nCount += aObjects->size();
if (nCount > 1) return;
}
// Check that there's exactly 1 image anchored in this node if (nCount == 1)
{
OString sTemp;
sText.convertToString(&sTemp, RTL_TEXTENCODING_ASCII_US, 0); if (sText.startsWith(SwResId(STR_POOLCOLL_LABEL))
|| sText.startsWith(SwResId(STR_POOLCOLL_LABEL_ABB))
|| sText.startsWith(SwResId(STR_POOLCOLL_LABEL_TABLE))
|| sText.startsWith(SwResId(STR_POOLCOLL_LABEL_FRAME))
|| sText.startsWith(SwResId(STR_POOLCOLL_LABEL_DRAWING))
|| sText.startsWith(SwResId(STR_POOLCOLL_LABEL_FIGURE)))
{ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_FAKE_CAPTIONS),
sfx::AccessibilityIssueID::FAKE_CAPTION,
sfx::AccessibilityIssueLevel::WARNLEV);
pIssue->setIssueObject(IssueObject::TEXT);
pIssue->setNode(pTextNode);
SwDoc& rDocument = pTextNode->GetDoc();
pIssue->setDoc(rDocument);
pIssue->setStart(0);
pIssue->setEnd(sText.getLength());
}
}
}
};
// ISO 142891-1 : 7.14 class NonInteractiveFormCheck : public NodeCheck
{ public:
NonInteractiveFormCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
: NodeCheck(rIssueCollection)
{
}
void check(SwNode* pCurrent) override
{ if (!pCurrent->IsTextNode()) return;
SwTextNode* pTextNode = pCurrent->GetTextNode(); constauto& text = pTextNode->GetText();
// Series of tests to detect if there are fake forms in the text. bool bCheck = text.indexOf("___") == -1; // Repeated underscores.
if (bCheck)
bCheck = text.indexOf("....") == -1; // Repeated dots.
if (bCheck)
bCheck = text.indexOf(u"……") == -1; // Repeated ellipsis.
if (bCheck)
bCheck = text.indexOf(u"….") == -1; // A dot after an ellipsis.
if (bCheck)
bCheck = text.indexOf(u".…") == -1; // An ellipsis after a dot.
// Checking if all the tests are passed successfully. If not, adding a warning. if (!bCheck)
{
sal_Int32 nStart = 0; auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_NON_INTERACTIVE_FORMS),
sfx::AccessibilityIssueID::NON_INTERACTIVE_FORMS,
sfx::AccessibilityIssueLevel::WARNLEV);
pIssue->setIssueObject(IssueObject::TEXT);
pIssue->setNode(pTextNode);
pIssue->setDoc(pTextNode->GetDoc());
pIssue->setStart(nStart);
pIssue->setEnd(nStart + text.getLength());
}
}
};
/// Check for floating text frames, as it causes problems with reading order. class FloatingTextCheck : public NodeCheck
{ public:
FloatingTextCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
: NodeCheck(rIssueCollection)
{
}
void check(SwNode* pCurrent) override
{ // if node is a text-node and if it has text, we proceed. Otherwise - return. const SwTextNode* textNode = pCurrent->GetTextNode(); if (!textNode || textNode->GetText().isEmpty()) return;
// If a node is in fly and if it is not anchored as char, throw warning. const SwNode* pStartFly = pCurrent->FindFlyStartNode(); if (!pStartFly) return;
const SwFrameFormat* pFormat = pStartFly->GetFlyFormat(); if (!pFormat) return;
/// Heading paragraphs (with outline levels > 0) are not allowed in tables class TableHeadingCheck : public NodeCheck
{ private: // Boolean indicating if heading-in-table warning is already triggered. bool m_bPrevPassed;
if (parentTable)
{
m_bPrevPassed = false; auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_HEADING_IN_TABLE),
sfx::AccessibilityIssueID::HEADING_IN_TABLE,
sfx::AccessibilityIssueLevel::WARNLEV);
pIssue->setIssueObject(IssueObject::TEXT);
pIssue->setDoc(pCurrent->GetDoc());
pIssue->setNode(pCurrent);
}
}
}
};
/// Checking if headings are ordered correctly. class HeadingOrderCheck : public NodeCheck
{ public:
HeadingOrderCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
: NodeCheck(rIssueCollection)
{
}
// Check for fontworks. if (SdrObjCustomShape const* pCustomShape = dynamic_cast<SdrObjCustomShape const*>(pObject))
{ const SdrCustomShapeGeometryItem& rGeometryItem
= pCustomShape->GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY);
if (const uno::Any* pAny = rGeometryItem.GetPropertyValueByName(u"Type"_ustr)) if (pAny->get<OUString>().startsWith("fontwork-"))
lclAddIssue(m_aIssueCollection, SwResId(STR_FONTWORKS),
sfx::AccessibilityIssueID::FONTWORKS,
sfx::AccessibilityIssueLevel::WARNLEV);
}
// Checking if there is floating Writer text draw object and if so, throwing a warning. // (Floating objects with text create problems with reading order) if (pObject->HasText()
&& FindFrameFormat(pObject)->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
{ auto pIssue = lclAddIssue(m_aIssueCollection, SwResId(STR_FLOATING_TEXT),
sfx::AccessibilityIssueID::FLOATING_TEXT,
sfx::AccessibilityIssueLevel::WARNLEV);
pIssue->setIssueObject(IssueObject::TEXTFRAME);
pIssue->setObjectID(pObject->GetName());
pIssue->setDoc(*m_pDoc); if (pCurrent)
pIssue->setNode(pCurrent);
}
// Graphic, OLE for alt (title) text already checked in NoTextNodeAltTextCheck if (pObject->GetObjIdentifier() != SdrObjKind::SwFlyDrawObjIdentifier)
{ if (!pObject->IsDecorative() && pObject->GetTitle().isEmpty()
&& pObject->GetDescription().isEmpty())
{ const OUString& sName = pObject->GetName();
OUString sIssueText = SwResId(STR_NO_ALT).replaceAll("%OBJECT_NAME%", sName); auto pIssue = lclAddIssue(m_aIssueCollection, sIssueText,
sfx::AccessibilityIssueID::NO_ALT_SHAPE,
sfx::AccessibilityIssueLevel::ERRORLEV); // Set FORM Issue for Form objects because of the design mode if (pObject->GetObjInventor() == SdrInventor::FmForm)
pIssue->setIssueObject(IssueObject::FORM); else
pIssue->setIssueObject(IssueObject::SHAPE);
pIssue->setObjectID(pObject->GetName());
pIssue->setDoc(*m_pDoc); if (pCurrent)
pIssue->setNode(pCurrent);
}
}
}
for (std::shared_ptr<BaseCheck>& rpNodeCheck : m_aNodeChecks)
{ auto pNodeCheck = dynamic_cast<NodeCheck*>(rpNodeCheck.get()); if (pNodeCheck)
pNodeCheck->check(pNode);
}
}
void AccessibilityCheck::checkDocumentProperties()
{ if (m_pDoc == nullptr) return;
init();
for (std::shared_ptr<BaseCheck>& rpDocumentCheck : m_aDocumentChecks)
{ auto pDocumentCheck = dynamic_cast<DocumentCheck*>(rpDocumentCheck.get()); if (pDocumentCheck)
pDocumentCheck->check(m_pDoc);
}
}
void AccessibilityCheck::check()
{ if (m_pDoc == nullptr) return;
init();
checkDocumentProperties();
autoconst& pNodes = m_pDoc->GetNodes();
SwNode* pNode = nullptr; for (SwNodeOffset n(0); n < pNodes.Count(); ++n)
{
pNode = pNodes[n]; if (pNode)
{ for (std::shared_ptr<BaseCheck>& rpNodeCheck : m_aNodeChecks)
{ auto pNodeCheck = dynamic_cast<NodeCheck*>(rpNodeCheck.get()); if (pNodeCheck)
pNodeCheck->check(pNode);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.