diff options
author | Colomban Wendling <cwendling@hypra.fr> | 2023-05-02 20:50:52 +0200 |
---|---|---|
committer | Michael Weghorn <m.weghorn@posteo.de> | 2023-05-23 18:39:08 +0200 |
commit | bd5c3582581f37513f45b518e348f443d5d57334 (patch) | |
tree | 6e9bce540afb614154d695b6e3280a198254b089 /sw/qa/extras | |
parent | dce8d2dbc48eb1c7597afec236dc51b4b8aede9c (diff) |
a11y: Fix returning unpaired surrogates when retrieving characters
Fix implementations of XAccessibleText's getTextAtIndex(),
getTextBeforeIndex() and getTextBehindIndex() when called with
AccessibleTextType::CHARACTER to return the whole code point rather
than an unpaired surrogate.
This is still not perfect because XAccessibleText::getCharacterCount()
will return an incorrect value (code units rather than code points),
but it fixes the most useful case of retrieving the character at e.g.
the caret offset.
This fixes the GTK3 and Windows backends as well without further
changes. Qt6 also mostly works according to Michael Weghorn, but for a
bug on Qt's side (https://bugreports.qt.io/browse/QTBUG-113438).
MacOS backend doesn't seem to be affected in the first place.
Change-Id: I53f07bcba78c6b267939257542a521b106101e96
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/151303
Tested-by: Jenkins
Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
Diffstat (limited to 'sw/qa/extras')
-rw-r--r-- | sw/qa/extras/accessibility/testdocuments/unicode.fodf | 135 | ||||
-rw-r--r-- | sw/qa/extras/accessibility/unicode.cxx | 102 |
2 files changed, 237 insertions, 0 deletions
diff --git a/sw/qa/extras/accessibility/testdocuments/unicode.fodf b/sw/qa/extras/accessibility/testdocuments/unicode.fodf new file mode 100644 index 000000000000..9bdccebf03ec --- /dev/null +++ b/sw/qa/extras/accessibility/testdocuments/unicode.fodf @@ -0,0 +1,135 @@ +<?xml version='1.0' encoding='UTF-8'?> +<office:document xmlns:css3t="http://www.w3.org/TR/css3-text/" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:rpt="http://openoffice.org/2005/report" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:meta><meta:creation-date>2022-10-12T18:05:31.408900485</meta:creation-date><dc:date>2023-05-11T10:35:16.229411275</dc:date><meta:editing-duration>PT9M45S</meta:editing-duration><meta:editing-cycles>3</meta:editing-cycles><meta:generator>LibreOfficeDev/7.6.0.0.alpha0$Linux_X86_64 LibreOffice_project/44c4d9ba0d480c8e2f05c9400f310184efc7e40c</meta:generator><meta:document-statistic meta:table-count="0" meta:image-count="0" meta:object-count="0" meta:page-count="1" meta:paragraph-count="2" meta:word-count="2" meta:character-count="8" meta:non-whitespace-character-count="8"/></office:meta> + <office:font-face-decls> + <style:font-face style:name="DejaVu Sans" svg:font-family="'DejaVu Sans'" style:font-family-generic="system" style:font-pitch="variable"/> + <style:font-face style:name="FreeSans1" svg:font-family="FreeSans" style:font-family-generic="system" style:font-pitch="variable"/> + <style:font-face style:name="Liberation Serif" svg:font-family="'Liberation Serif'" style:font-family-generic="roman" style:font-pitch="variable"/> + <style:font-face style:name="Quivira" svg:font-family="Quivira" style:font-pitch="variable"/> + </office:font-face-decls> + <office:styles> + <style:default-style style:family="graphic"> + <style:graphic-properties svg:stroke-color="#3465a4" draw:fill-color="#729fcf" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" draw:start-line-spacing-vertical="0.283cm" draw:end-line-spacing-horizontal="0.283cm" draw:end-line-spacing-vertical="0.283cm" style:writing-mode="lr-tb" style:flow-with-text="false"/> + <style:paragraph-properties style:text-autospace="ideograph-alpha" style:line-break="strict" loext:tab-stop-distance="0cm" style:writing-mode="lr-tb" style:font-independent-line-spacing="false"> + <style:tab-stops/> + </style:paragraph-properties> + <style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="fr" fo:country="FR" style:letter-kerning="true" style:font-name-asian="DejaVu Sans" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="FreeSans1" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN"/> + </style:default-style> + <style:default-style style:family="paragraph"> + <style:paragraph-properties fo:orphans="2" fo:widows="2" fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" style:punctuation-wrap="hanging" style:line-break="strict" style:tab-stop-distance="1.251cm" style:writing-mode="page"/> + <style:text-properties style:use-window-font-color="true" loext:opacity="0%" style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="fr" fo:country="FR" style:letter-kerning="true" style:font-name-asian="DejaVu Sans" style:font-size-asian="10.5pt" style:language-asian="zh" style:country-asian="CN" style:font-name-complex="FreeSans1" style:font-size-complex="12pt" style:language-complex="hi" style:country-complex="IN" fo:hyphenate="false" fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" loext:hyphenation-no-caps="false" loext:hyphenation-no-last-word="false" loext:hyphenation-word-char-count="5" loext:hyphenation-zone="no-limit"/> + </style:default-style> + <style:default-style style:family="table"> + <style:table-properties table:border-model="collapsing"/> + </style:default-style> + <style:default-style style:family="table-row"> + <style:table-row-properties fo:keep-together="auto"/> + </style:default-style> + <style:style style:name="Standard" style:family="paragraph" style:class="text"/> + <text:outline-style style:name="Outline"> + <text:outline-level-style text:level="1" loext:num-list-format="%1%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="2" loext:num-list-format="%2%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="3" loext:num-list-format="%3%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="4" loext:num-list-format="%4%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="5" loext:num-list-format="%5%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="6" loext:num-list-format="%6%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="7" loext:num-list-format="%7%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="8" loext:num-list-format="%8%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="9" loext:num-list-format="%9%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + <text:outline-level-style text:level="10" loext:num-list-format="%10%" style:num-format=""> + <style:list-level-properties text:list-level-position-and-space-mode="label-alignment"> + <style:list-level-label-alignment text:label-followed-by="listtab"/> + </style:list-level-properties> + </text:outline-level-style> + </text:outline-style> + <text:notes-configuration text:note-class="footnote" style:num-format="1" text:start-value="0" text:footnotes-position="page" text:start-numbering-at="document"/> + <text:notes-configuration text:note-class="endnote" style:num-format="i" text:start-value="0"/> + <text:linenumbering-configuration text:number-lines="false" text:offset="0.499cm" style:num-format="1" text:number-position="left" text:increment="5"/> + <loext:theme loext:name="Office Theme"> + <loext:color-table loext:name="LibreOffice"> + <loext:color loext:name="dk1" loext:color="#000000"/> + <loext:color loext:name="lt1" loext:color="#ffffff"/> + <loext:color loext:name="dk2" loext:color="#000000"/> + <loext:color loext:name="lt2" loext:color="#ffffff"/> + <loext:color loext:name="accent1" loext:color="#18a303"/> + <loext:color loext:name="accent2" loext:color="#0369a3"/> + <loext:color loext:name="accent3" loext:color="#a33e03"/> + <loext:color loext:name="accent4" loext:color="#8e03a3"/> + <loext:color loext:name="accent5" loext:color="#c99c00"/> + <loext:color loext:name="accent6" loext:color="#c9211e"/> + <loext:color loext:name="hlink" loext:color="#0000ee"/> + <loext:color loext:name="folHlink" loext:color="#551a8b"/> + </loext:color-table> + </loext:theme> + </office:styles> + <office:automatic-styles> + <style:style style:name="P1" style:family="paragraph" style:parent-style-name="Standard"> + <style:text-properties style:font-name="Quivira" fo:font-size="24pt" style:font-name-asian="Quivira" style:font-size-asian="24pt" style:font-name-complex="Quivira" style:font-size-complex="24pt"/> + </style:style> + <style:style style:name="P2" style:family="paragraph" style:parent-style-name="Standard"> + <style:text-properties style:font-name="Quivira" fo:font-size="24pt" style:font-name-asian="Quivira" style:font-size-asian="24pt" style:font-name-complex="Quivira" style:font-size-complex="24pt"/> + </style:style> + <style:page-layout style:name="pm1"> + <style:page-layout-properties fo:page-width="21.001cm" fo:page-height="29.7cm" style:num-format="1" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb" style:layout-grid-color="#c0c0c0" style:layout-grid-lines="20" style:layout-grid-base-height="0.706cm" style:layout-grid-ruby-height="0.353cm" style:layout-grid-mode="none" style:layout-grid-ruby-below="false" style:layout-grid-print="false" style:layout-grid-display="false" style:footnote-max-height="0cm" loext:margin-gutter="0cm"> + <style:footnote-sep style:width="0.018cm" style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" style:line-style="solid" style:adjustment="left" style:rel-width="25%" style:color="#000000"/> + </style:page-layout-properties> + <style:header-style/> + <style:footer-style/> + </style:page-layout> + <style:style style:name="dp1" style:family="drawing-page"> + <style:drawing-page-properties draw:background-size="full"/> + </style:style> + </office:automatic-styles> + <office:master-styles> + <style:master-page style:name="Standard" style:page-layout-name="pm1" draw:style-name="dp1"/> + </office:master-styles> + <office:body> + <office:text> + <text:sequence-decls> + <text:sequence-decl text:display-outline-level="0" text:name="Illustration"/> + <text:sequence-decl text:display-outline-level="0" text:name="Table"/> + <text:sequence-decl text:display-outline-level="0" text:name="Text"/> + <text:sequence-decl text:display-outline-level="0" text:name="Drawing"/> + <text:sequence-decl text:display-outline-level="0" text:name="Figure"/> + </text:sequence-decls> + <text:p text:style-name="P1">🂡🂮🂬🂫</text:p> + <text:p text:style-name="P2">akcj</text:p> + </office:text> + </office:body> +</office:document>
\ No newline at end of file diff --git a/sw/qa/extras/accessibility/unicode.cxx b/sw/qa/extras/accessibility/unicode.cxx new file mode 100644 index 000000000000..b4b2b5f6fc84 --- /dev/null +++ b/sw/qa/extras/accessibility/unicode.cxx @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#include <com/sun/star/accessibility/AccessibleTextType.hpp> +#include <com/sun/star/accessibility/XAccessibleText.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + +#include <test/a11y/swaccessibletestbase.hxx> + +using namespace css; +using namespace accessibility; + +// Checks fetching multi-unit characters +CPPUNIT_TEST_FIXTURE(test::SwAccessibleTestBase, TestUnicodeSP) +{ + loadFromSrc(u"/sw/qa/extras/accessibility/testdocuments/unicode.fodf"); + + auto xContext = getDocumentAccessibleContext()->getAccessibleChild(0)->getAccessibleContext(); + + uno::Reference<XAccessibleText> para(xContext, uno::UNO_QUERY_THROW); + auto segment = para->getTextAtIndex(0, AccessibleTextType::CHARACTER); + CPPUNIT_ASSERT_EQUAL(OUString(u"\U0001f0a1"), segment.SegmentText); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), segment.SegmentStart); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), segment.SegmentEnd); + + segment = para->getTextBeforeIndex(2, AccessibleTextType::CHARACTER); + CPPUNIT_ASSERT_EQUAL(OUString(u"\U0001f0a1"), segment.SegmentText); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), segment.SegmentStart); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), segment.SegmentEnd); + + segment = para->getTextBehindIndex(0, AccessibleTextType::CHARACTER); + CPPUNIT_ASSERT_EQUAL(OUString(u"\U0001f0ae"), segment.SegmentText); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), segment.SegmentStart); + CPPUNIT_ASSERT_EQUAL(sal_Int32(4), segment.SegmentEnd); +} + +// Checks getTextBehindIndex() with multi-unit characters +CPPUNIT_TEST_FIXTURE(test::SwAccessibleTestBase, TestUnicodeSPBehindIndex) +{ + loadFromSrc(u"/sw/qa/extras/accessibility/testdocuments/unicode.fodf"); + + auto xContext = getDocumentAccessibleContext()->getAccessibleChild(0)->getAccessibleContext(); + + uno::Reference<XAccessibleText> para(xContext, uno::UNO_QUERY_THROW); + auto nChCount = para->getCharacterCount(); + + // verify bounds are properly handled + CPPUNIT_ASSERT_THROW(para->getTextBehindIndex(-1, AccessibleTextType::CHARACTER), + lang::IndexOutOfBoundsException); + CPPUNIT_ASSERT_THROW(para->getTextBehindIndex(nChCount + 1, AccessibleTextType::CHARACTER), + lang::IndexOutOfBoundsException); + + auto segment = para->getTextBehindIndex(nChCount, AccessibleTextType::CHARACTER); + CPPUNIT_ASSERT_EQUAL(OUString(u""), segment.SegmentText); + CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), segment.SegmentStart); + CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), segment.SegmentEnd); + + segment = para->getTextBehindIndex(nChCount - 2, AccessibleTextType::CHARACTER); + CPPUNIT_ASSERT_EQUAL(OUString(u""), segment.SegmentText); + CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), segment.SegmentStart); + CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), segment.SegmentEnd); + + segment = para->getTextBehindIndex(nChCount - 4, AccessibleTextType::CHARACTER); + CPPUNIT_ASSERT_EQUAL(OUString(u"\U0001f0ab"), segment.SegmentText); + CPPUNIT_ASSERT_EQUAL(sal_Int32(6), segment.SegmentStart); + CPPUNIT_ASSERT_EQUAL(sal_Int32(8), segment.SegmentEnd); + + // verify bounds behave the same with single unit characters, just as a validation + xContext = getNextFlowingSibling(xContext); + CPPUNIT_ASSERT(xContext.is()); + para.set(xContext, uno::UNO_QUERY_THROW); + + nChCount = para->getCharacterCount(); + + CPPUNIT_ASSERT_THROW(para->getTextBehindIndex(-1, AccessibleTextType::CHARACTER), + lang::IndexOutOfBoundsException); + CPPUNIT_ASSERT_THROW(para->getTextBehindIndex(nChCount + 1, AccessibleTextType::CHARACTER), + lang::IndexOutOfBoundsException); + + segment = para->getTextBehindIndex(nChCount, AccessibleTextType::CHARACTER); + CPPUNIT_ASSERT_EQUAL(OUString(u""), segment.SegmentText); + CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), segment.SegmentStart); + CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), segment.SegmentEnd); + + segment = para->getTextBehindIndex(nChCount - 1, AccessibleTextType::CHARACTER); + CPPUNIT_ASSERT_EQUAL(OUString(u""), segment.SegmentText); + CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), segment.SegmentStart); + CPPUNIT_ASSERT_EQUAL(sal_Int32(-1), segment.SegmentEnd); + + segment = para->getTextBehindIndex(nChCount - 2, AccessibleTextType::CHARACTER); + CPPUNIT_ASSERT_EQUAL(OUString(u"j"), segment.SegmentText); + CPPUNIT_ASSERT_EQUAL(sal_Int32(3), segment.SegmentStart); + CPPUNIT_ASSERT_EQUAL(sal_Int32(4), segment.SegmentEnd); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ |