/* -*- 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;
}
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.