cui/inc/strings.hrc | 1 cui/source/options/optaccessibility.cxx | 9 + include/sfx2/AccessibilityIssue.hxx | 1 officecfg/registry/schema/org/openoffice/Office/Common.xcs | 10 + sw/inc/AccessibilityCheckStrings.hrc | 1 sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx | 15 ++ sw/qa/core/accessibilitycheck/data/NestedLinks.odt |binary sw/source/core/access/AccessibilityCheck.cxx | 89 +++++++++++-- sw/source/uibase/sidebar/A11yCheckIssuesPanel.cxx | 1 9 files changed, 115 insertions(+), 12 deletions(-)
New commits: commit 2cc3a9f4c8c2359b525261ec917bb68fc5e8b6c4 Author: Balazs Varga <balazs.varga.ext...@allotropia.de> AuthorDate: Thu Mar 13 13:49:02 2025 +0100 Commit: Balazs Varga <balazs.varga.ext...@allotropia.de> CommitDate: Mon Mar 17 08:36:42 2025 +0100 tdf#164797 - sw a11y: add new check of links and references in Header/Footer to warn users about PAC "Link annotation is not nested inside a Link structure element." errors, so they can avoid them. Change-Id: I8d1fc7042e2ee34a5777c3a448d2379038ea24e9 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182868 Tested-by: Jenkins Reviewed-by: Balazs Varga <balazs.varga.ext...@allotropia.de> diff --git a/cui/inc/strings.hrc b/cui/inc/strings.hrc index a6d08758a626..5a42bd184054 100644 --- a/cui/inc/strings.hrc +++ b/cui/inc/strings.hrc @@ -553,6 +553,7 @@ #define STR_HYPERLINK_TEXT_IS_LINK NC_("STR_HYPERLINK_TEXT_IS_LINK", "Check if the hyperlink text is the same as the link address.") #define STR_HYPERLINK_TEXT_IS_SHORT NC_("STR_HYPERLINK_TEXT_IS_SHORT", "Check if the hyperlink text is too short.") #define STR_HYPERLINK_NO_NAME NC_("STR_HYPERLINK_NO_NAME", "Check if the hyperlink has an alternative name set.") +#define STR_LINK_TEXT_IS_NOT_NESTED NC_("STR_LINK_TEXT_IS_NOT_NESTED", "Check if links and reference fields are used in the header or footer.") #define STR_TEXT_CONTRAST NC_("STR_TEXT_CONTRAST", "Check if text contrast is high enough.") #define STR_TEXT_BLINKING NC_("STR_TEXT_BLINKING", "Check if the document contains blinking text.") #define STR_AVOID_FOOTNOTES NC_("STR_AVOID_FOOTNOTES", "Check if the document contains footnotes.") diff --git a/cui/source/options/optaccessibility.cxx b/cui/source/options/optaccessibility.cxx index 27971df71360..79bbccde8e6c 100644 --- a/cui/source/options/optaccessibility.cxx +++ b/cui/source/options/optaccessibility.cxx @@ -47,6 +47,7 @@ namespace { u"HyperlinkText"_ustr, { sfx::AccessibilityIssueID::HYPERLINK_IS_TEXT, STR_HYPERLINK_TEXT_IS_LINK } }, { u"HyperlinkShort"_ustr, { sfx::AccessibilityIssueID::HYPERLINK_SHORT, STR_HYPERLINK_TEXT_IS_SHORT } }, { u"HyperlinkNoName"_ustr, { sfx::AccessibilityIssueID::HYPERLINK_NO_NAME, STR_HYPERLINK_NO_NAME } }, + { u"LinkInHeaderOrFooter"_ustr, { sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER, STR_LINK_TEXT_IS_NOT_NESTED } }, { u"FakeFootnotes"_ustr, { sfx::AccessibilityIssueID::FAKE_FOOTNOTE, STR_AVOID_FAKE_FOOTNOTES } }, { u"FakeCaptions"_ustr, { sfx::AccessibilityIssueID::FAKE_CAPTION, STR_AVOID_FAKE_CAPTIONS } }, { u"ManualNumbering"_ustr, { sfx::AccessibilityIssueID::MANUAL_NUMBERING, STR_FAKE_NUMBERING } }, @@ -241,6 +242,10 @@ bool SvxAccessibilityOptionsTabPage::FillItemSet( SfxItemSet* ) officecfg::Office::Common::AccessibilityIssues::HyperlinkNoName::set(bChecked, batch); break; + case sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER: + officecfg::Office::Common::AccessibilityIssues::LinkInHeaderOrFooter::set(bChecked, batch); + break; + case sfx::AccessibilityIssueID::FAKE_FOOTNOTE: officecfg::Office::Common::AccessibilityIssues::FakeFootnotes::set(bChecked, batch); break; @@ -449,6 +454,10 @@ void SvxAccessibilityOptionsTabPage::Reset( const SfxItemSet* ) bChecked = officecfg::Office::Common::AccessibilityIssues::HyperlinkNoName::get(); break; + case sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER: + bChecked = officecfg::Office::Common::AccessibilityIssues::LinkInHeaderOrFooter::get(); + break; + case sfx::AccessibilityIssueID::FAKE_FOOTNOTE: bChecked = officecfg::Office::Common::AccessibilityIssues::FakeFootnotes::get(); break; diff --git a/include/sfx2/AccessibilityIssue.hxx b/include/sfx2/AccessibilityIssue.hxx index 65174e0fdfdb..18bfadf288be 100644 --- a/include/sfx2/AccessibilityIssue.hxx +++ b/include/sfx2/AccessibilityIssue.hxx @@ -40,6 +40,7 @@ enum class AccessibilityIssueID HYPERLINK_IS_TEXT, HYPERLINK_SHORT, HYPERLINK_NO_NAME, + LINK_IN_HEADER_FOOTER, FAKE_FOOTNOTE, FAKE_CAPTION, MANUAL_NUMBERING, diff --git a/officecfg/registry/schema/org/openoffice/Office/Common.xcs b/officecfg/registry/schema/org/openoffice/Office/Common.xcs index 5358b7e3c20f..a7fedae390a2 100644 --- a/officecfg/registry/schema/org/openoffice/Office/Common.xcs +++ b/officecfg/registry/schema/org/openoffice/Office/Common.xcs @@ -6411,6 +6411,16 @@ </info> <value>true</value> </prop> + <prop oor:name="LinkInHeaderOrFooter" oor:type="xs:boolean" oor:nillable="false"> + <info> + <desc> + Enables accessibility check of links and reference fields are used in the + header or footer. + </desc> + <label>Enable/Disable not nested link structure elements accessibility check.</label> + </info> + <value>true</value> + </prop> <prop oor:name="FakeFootnotes" oor:type="xs:boolean" oor:nillable="false"> <info> <desc> diff --git a/sw/inc/AccessibilityCheckStrings.hrc b/sw/inc/AccessibilityCheckStrings.hrc index 6acfa0761157..6c8304f1773b 100644 --- a/sw/inc/AccessibilityCheckStrings.hrc +++ b/sw/inc/AccessibilityCheckStrings.hrc @@ -20,6 +20,7 @@ #define STR_HYPERLINK_TEXT_IS_LINK NC_("STR_HYPERLINK_TEXT_IS_LINK", "Hyperlink text is the same as the link address “%LINK%”.") #define STR_HYPERLINK_TEXT_IS_SHORT NC_("STR_HYPERLINK_TEXT_IS_SHORT", "Hyperlink text is too short.") #define STR_HYPERLINK_NO_NAME NC_("STR_HYPERLINK_NO_NAME", "Missing 'Name' property of hyperlink.") +#define STR_LINK_TEXT_IS_NOT_NESTED NC_("STR_LINK_TEXT_IS_NOT_NESTED", "Avoid using links and reference fields in the header or footer.") #define STR_TEXT_CONTRAST NC_("STR_TEXT_CONTRAST", "Text contrast is too low.") #define STR_TEXT_BLINKING NC_("STR_TEXT_BLINKING", "Blinking text.") #define STR_AVOID_FOOTNOTES NC_("STR_AVOID_FOOTNOTES", "Avoid footnotes.") diff --git a/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx b/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx index 981cf03cd26b..ff381355fe73 100644 --- a/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx +++ b/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx @@ -155,6 +155,21 @@ CPPUNIT_TEST_FIXTURE(AccessibilityCheckTest, testHyperlinks) CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::DIRECT_FORMATTING, aIssues[4]->m_eIssueID); } +CPPUNIT_TEST_FIXTURE(AccessibilityCheckTest, testNestedlinks) +{ + createSwDoc("NestedLinks.odt"); + SwDoc* pDoc = getSwDoc(); + sw::AccessibilityCheck aCheck(pDoc); + aCheck.check(); + auto& aIssues = aCheck.getIssueCollection().getIssues(); + CPPUNIT_ASSERT_EQUAL(size_t(3), aIssues.size()); + CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER, aIssues[0]->m_eIssueID); + CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueLevel::WARNLEV, aIssues[0]->m_eIssueLvl); + CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER, aIssues[1]->m_eIssueID); + CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueLevel::WARNLEV, aIssues[1]->m_eIssueLvl); + CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER, aIssues[2]->m_eIssueID); +} + CPPUNIT_TEST_FIXTURE(AccessibilityCheckTest, testCheckHighlightedText) { createSwDoc("HighlightTest.odt"); diff --git a/sw/qa/core/accessibilitycheck/data/NestedLinks.odt b/sw/qa/core/accessibilitycheck/data/NestedLinks.odt new file mode 100644 index 000000000000..45e251eeb9da Binary files /dev/null and b/sw/qa/core/accessibilitycheck/data/NestedLinks.odt differ diff --git a/sw/source/core/access/AccessibilityCheck.cxx b/sw/source/core/access/AccessibilityCheck.cxx index 3985683f65ff..a5a940865d1d 100644 --- a/sw/source/core/access/AccessibilityCheck.cxx +++ b/sw/source/core/access/AccessibilityCheck.cxx @@ -37,6 +37,7 @@ #include <svx/xfillit0.hxx> #include <svx/xflclit.hxx> #include <ftnidx.hxx> +#include <authfld.hxx> #include <txtftn.hxx> #include <txtfrm.hxx> #include <svl/itemiter.hxx> @@ -190,6 +191,12 @@ void lcl_SetHiddenIssues(std::shared_ptr<sw::AccessibilityIssue>& pIssue) 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()) @@ -603,6 +610,7 @@ private: for (size_t i = 0; i < rHints.Count(); ++i) { const SwTextAttr* pTextAttr = rHints.Get(i); + SwDoc& rDocument = pTextNode->GetDoc(); if (pTextAttr->Which() == RES_TXTATR_INETFMT) { OUString sHyperlink = pTextAttr->GetINetFormat().GetValue(); @@ -634,7 +642,6 @@ private: { pIssue->setIssueObject(IssueObject::TEXT); pIssue->setNode(pTextNode); - SwDoc& rDocument = pTextNode->GetDoc(); pIssue->setDoc(rDocument); pIssue->setStart(nStart); pIssue->setEnd(nStart + sRunText.getLength()); @@ -645,22 +652,80 @@ private: OUString sHyperlinkName = pTextAttr->GetINetFormat().GetName(); if (sHyperlinkName.isEmpty()) { - std::shared_ptr<sw::AccessibilityIssue> pNameIssue - = lclAddIssue(m_rIssueCollection, SwResId(STR_HYPERLINK_NO_NAME), - sfx::AccessibilityIssueID::HYPERLINK_NO_NAME, - sfx::AccessibilityIssueLevel::WARNLEV); + pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_HYPERLINK_NO_NAME), + sfx::AccessibilityIssueID::HYPERLINK_NO_NAME, + sfx::AccessibilityIssueLevel::WARNLEV); - if (pNameIssue) + if (pIssue) { - pNameIssue->setIssueObject(IssueObject::HYPERLINKTEXT); - pNameIssue->setNode(pTextNode); - SwDoc& rDocument = pTextNode->GetDoc(); - pNameIssue->setDoc(rDocument); - pNameIssue->setStart(nStart); - pNameIssue->setEnd(nStart + sRunText.getLength()); + pIssue->setIssueObject(IssueObject::HYPERLINKTEXT); + pIssue->setNode(pTextNode); + pIssue->setDoc(rDocument); + pIssue->setStart(nStart); + pIssue->setEnd(nStart + sRunText.getLength()); } } } + + // 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); + + if (pIssue) + { + pIssue->setIssueObject(IssueObject::TEXT); + pIssue->setNode(pTextNode); + pIssue->setDoc(rDocument); + pIssue->setStart(nStart); + pIssue->setEnd(nStart + sRunText.getLength()); + } + } + } + } + + // 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; + } + else if (SwFieldIds::TableOfAuthorities == pField->Which()) + { + const auto& 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); + + if (pIssue) + { + sal_Int32 nStart = pTextAttr->GetStart(); + pIssue->setIssueObject(IssueObject::TEXT); + pIssue->setNode(pTextNode); + pIssue->setDoc(rDocument); + pIssue->setStart(nStart); + pIssue->setEnd(nStart + 1); + } } } } diff --git a/sw/source/uibase/sidebar/A11yCheckIssuesPanel.cxx b/sw/source/uibase/sidebar/A11yCheckIssuesPanel.cxx index a898f9c5384d..6f68a99949c0 100644 --- a/sw/source/uibase/sidebar/A11yCheckIssuesPanel.cxx +++ b/sw/source/uibase/sidebar/A11yCheckIssuesPanel.cxx @@ -416,6 +416,7 @@ void A11yCheckIssuesPanel::populateIssues() case sfx::AccessibilityIssueID::AVOID_FOOTNOTES: case sfx::AccessibilityIssueID::AVOID_ENDNOTES: case sfx::AccessibilityIssueID::FONTWORKS: + case sfx::AccessibilityIssueID::LINK_IN_HEADER_FOOTER: { if (!pIssue->getHidden()) addEntryForGroup(AccessibilityCheckGroups::Other, nIndices, pIssue);