a11y: Add check for tabs used for formatting

Add accessibility check and relevant test for a document that uses
tabs for formatting

Useful to detect fake tables made of tabs

Change-Id: Ief765f25c8dc67405d0671e257cf0ba7aec9f16c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/141732
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <[email protected]>
diff --git a/sw/inc/AccessibilityCheckStrings.hrc b/sw/inc/AccessibilityCheckStrings.hrc
index 7245a2b..baa61cb 100644
--- a/sw/inc/AccessibilityCheckStrings.hrc
+++ b/sw/inc/AccessibilityCheckStrings.hrc
@@ -27,6 +27,7 @@
#define STR_AVOID_BACKGROUND_IMAGES     NC_("STR_AVOID_BACKGROUND_IMAGES", "Avoid background images.")
#define STR_AVOID_NEWLINES_SPACE        NC_("STR_AVOID_NEWLINES_SPACE", "Avoid newlines to create space.")
#define STR_AVOID_SPACES_SPACE          NC_("STR_AVOID_SPACES_SPACE", "Avoid spaces to create space.")
#define STR_AVOID_TABS_FORMATTING       NC_("STR_AVOID_TABS_FORMATTING", "Avoid using tabs for formatting.")
#define STR_HEADINGS_NOT_IN_ORDER       NC_("STR_HEADINGS_NOT_IN_ORDER", "Headings not in order.")
#define STR_TEXT_FORMATTING_CONVEYS_MEANING NC_("STR_TEXT_FORMATTING_CONVEYS_MEANING", "The text formatting conveys additional meaning.")
#define STR_NON_INTERACTIVE_FORMS       NC_("STR_NON_INTERACTIVE_FORMS", "An input form is not interactive.")
diff --git a/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx b/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx
index cc8b374..9397832 100644
--- a/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx
+++ b/sw/qa/core/accessibilitycheck/AccessibilityCheckTest.cxx
@@ -168,6 +168,20 @@
    CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::TABLE_FORMATTING, aIssues[0]->m_eIssueID);
}

CPPUNIT_TEST_FIXTURE(AccessibilityCheckTest, testCheckTabsFormatting)
{
    SwDoc* pDoc = createSwDoc("TabsTest.odt");
    CPPUNIT_ASSERT(pDoc);
    sw::AccessibilityCheck aCheck(pDoc);
    aCheck.check();
    auto& aIssues = aCheck.getIssueCollection().getIssues();
    CPPUNIT_ASSERT_EQUAL(size_t(4), aIssues.size());
    CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::TEXT_FORMATTING, aIssues[0]->m_eIssueID);
    CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::TEXT_FORMATTING, aIssues[1]->m_eIssueID);
    CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::TEXT_FORMATTING, aIssues[2]->m_eIssueID);
    CPPUNIT_ASSERT_EQUAL(sfx::AccessibilityIssueID::TEXT_FORMATTING, aIssues[3]->m_eIssueID);
}

CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/core/accessibilitycheck/data/TabsTest.odt b/sw/qa/core/accessibilitycheck/data/TabsTest.odt
new file mode 100644
index 0000000..29b415d
--- /dev/null
+++ b/sw/qa/core/accessibilitycheck/data/TabsTest.odt
Binary files differ
diff --git a/sw/source/core/access/AccessibilityCheck.cxx b/sw/source/core/access/AccessibilityCheck.cxx
index ce0409d..897ee0d4 100644
--- a/sw/source/core/access/AccessibilityCheck.cxx
+++ b/sw/source/core/access/AccessibilityCheck.cxx
@@ -791,33 +791,63 @@
        const OUString& sParagraphText = pTextNode->GetText();
        sal_Int32 nSpaceCount = 0;
        sal_Int32 nSpaceStart = 0;
        sal_Int32 nTabCount = 0;
        bool bNonSpaceFound = false;
        bool bPreviousWasChar = false;
        for (sal_Int32 i = 0; i < nParagraphLength; i++)
        {
            if (sParagraphText[i] == ' ')
            switch (sParagraphText[i])
            {
                if (bNonSpaceFound)
                case ' ':
                {
                    nSpaceCount++;
                    if (nSpaceCount == 2)
                        nSpaceStart = i;
                    if (bNonSpaceFound)
                    {
                        nSpaceCount++;
                        if (nSpaceCount == 2)
                            nSpaceStart = i;
                    }
                    break;
                }
            }
            else
            {
                if (nSpaceCount >= 2)
                case '\t':
                {
                    auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_SPACES_SPACE),
                                              sfx::AccessibilityIssueID::TEXT_FORMATTING);
                    pIssue->setIssueObject(IssueObject::TEXT);
                    pIssue->setNode(pTextNode);
                    SwDoc& rDocument = pTextNode->GetDoc();
                    pIssue->setDoc(rDocument);
                    pIssue->setStart(nSpaceStart);
                    pIssue->setEnd(nSpaceStart + nSpaceCount - 1);
                    if (bPreviousWasChar)
                    {
                        ++nTabCount;
                        bPreviousWasChar = false;
                        if (nTabCount == 2)
                        {
                            auto pIssue = lclAddIssue(m_rIssueCollection,
                                                      SwResId(STR_AVOID_TABS_FORMATTING),
                                                      sfx::AccessibilityIssueID::TEXT_FORMATTING);
                            pIssue->setIssueObject(IssueObject::TEXT);
                            pIssue->setNode(pTextNode);
                            SwDoc& rDocument = pTextNode->GetDoc();
                            pIssue->setDoc(rDocument);
                            pIssue->setStart(0);
                            pIssue->setEnd(nParagraphLength);
                        }
                    }
                    break;
                }
                bNonSpaceFound = true;
                nSpaceCount = 0;
                default:
                {
                    if (nSpaceCount >= 2)
                    {
                        auto pIssue
                            = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_SPACES_SPACE),
                                          sfx::AccessibilityIssueID::TEXT_FORMATTING);
                        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;
                    nSpaceCount = 0;
                    break;
                }
            }
        }
    }