tdf#116883: sw: support for lists level format string
Multilevel lists are more flexible in case of DOCX. There is
supported custom format for any level in DOCX unlike in LO
and ODT where we are limited only with prefix and suffix
for hardcoded list levels separated by dot. At the same time
DOCX can have lists not only "1.2.3.4", but "1/2/3/4" or even
"1!2>3)4" and such format can vary on each list level.
Here is basic implementation for list format as a core feature
for all documents and old way (prefix-suffix + ".") is left
as fallback.
Practically its usage is currently implemented only in DOCX
import/export.
Some RTF/OOXML unittests were redesigned: since we are not creating
prefix/suffix for these formats conditions should be checked in
a different way.
Change-Id: I1ec58bcc5874d4fa19aee6a1f42bf1671d853b14
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/92106
Tested-by: Jenkins
Reviewed-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/93125
Tested-by: Jenkins
Reviewed-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>
Change-Id: Ia8f066913a2565559d81f3caabeba24b29c09052
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/93456
diff --git a/editeng/source/items/numitem.cxx b/editeng/source/items/numitem.cxx
index 67cf037..6182ec5 100644
--- a/editeng/source/items/numitem.cxx
+++ b/editeng/source/items/numitem.cxx
@@ -344,9 +344,10 @@ SvxNumberFormat& SvxNumberFormat::operator=( const SvxNumberFormat& rFormat )
mnListtabPos = rFormat.mnListtabPos;
mnFirstLineIndent = rFormat.mnFirstLineIndent;
mnIndentAt = rFormat.mnIndentAt;
eVertOrient = rFormat.eVertOrient ;
sPrefix = rFormat.sPrefix ;
sSuffix = rFormat.sSuffix ;
eVertOrient = rFormat.eVertOrient;
sPrefix = rFormat.sPrefix;
sSuffix = rFormat.sSuffix;
sListFormat = rFormat.sListFormat;
aGraphicSize = rFormat.aGraphicSize ;
nBulletColor = rFormat.nBulletColor ;
nBulletRelSize = rFormat.nBulletRelSize;
@@ -381,6 +382,7 @@ bool SvxNumberFormat::operator==( const SvxNumberFormat& rFormat) const
eVertOrient != rFormat.eVertOrient ||
sPrefix != rFormat.sPrefix ||
sSuffix != rFormat.sSuffix ||
sListFormat != rFormat.sListFormat ||
aGraphicSize != rFormat.aGraphicSize ||
nBulletColor != rFormat.nBulletColor ||
nBulletRelSize != rFormat.nBulletRelSize ||
diff --git a/include/editeng/numitem.hxx b/include/editeng/numitem.hxx
index 699f829..52cb148 100644
--- a/include/editeng/numitem.hxx
+++ b/include/editeng/numitem.hxx
@@ -102,6 +102,7 @@ public:
private:
OUString sPrefix;
OUString sSuffix;
OUString sListFormat; // Format string ">%1.%2<" can be used instead of prefix/suffix
SvxAdjust eNumAdjust;
@@ -166,6 +167,8 @@ public:
const OUString& GetPrefix() const { return sPrefix;}
void SetSuffix(const OUString& rSet) { sSuffix = rSet;}
const OUString& GetSuffix() const { return sSuffix;}
void SetListFormat(const OUString& rSet) { sListFormat = rSet; }
const OUString& GetListFormat() const { return sListFormat; }
void SetCharFormatName(const OUString& rSet){ sCharStyleName = rSet; }
virtual OUString GetCharFormatName()const;
diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx
index d324be6..91780b6 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -77,6 +77,7 @@
#define UNO_NAME_ANCHOR_CHAR_STYLE_NAME "AnchorCharStyleName"
#define UNO_NAME_SUFFIX "Suffix"
#define UNO_NAME_PREFIX "Prefix"
#define UNO_NAME_LIST_FORMAT "ListFormat"
#define UNO_NAME_PARENT_NUMBERING "ParentNumbering"
#define UNO_NAME_CHAR_FONT_NAME "CharFontName"
#define UNO_NAME_CHAR_FONT_STYLE_NAME "CharFontStyleName"
diff --git a/sw/qa/extras/ooxmlexport/data/tdf116883.docx b/sw/qa/extras/ooxmlexport/data/tdf116883.docx
new file mode 100644
index 0000000..52a9c42
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf116883.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
index e144e61..d4a453a 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx
@@ -780,7 +780,10 @@ DECLARE_OOXMLEXPORT_TEST(testNumOverrideLvltext, "num-override-lvltext.docx")
{
uno::Reference<container::XIndexAccess> xRules = getProperty< uno::Reference<container::XIndexAccess> >(getStyles("NumberingStyles")->getByName("WWNum1"), "NumberingRules");
// This was 1, i.e. the numbering on the second level was "1", not "1.1".
CPPUNIT_ASSERT_EQUAL(sal_Int16(2), comphelper::SequenceAsHashMap(xRules->getByIndex(1))["ParentNumbering"].get<sal_Int16>());
// Check the praragraph properties, not the list ones, since they can differ due to overrides
uno::Reference<beans::XPropertySet> xPara(getParagraph(1), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(1), getProperty<sal_Int16>(xPara, "NumberingLevel"));
CPPUNIT_ASSERT_EQUAL(OUString("1.1"), getProperty<OUString>(xPara, "ListLabelString"));
// The paragraph marker's red font color was inherited by the number portion, this was ff0000.
CPPUNIT_ASSERT_EQUAL(OUString("ffffffff"), parseDump("//Special[@nType='PortionType::Number']/SwFont", "color"));
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx
index 835947d..92d7d63 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport10.cxx
@@ -853,25 +853,11 @@ DECLARE_OOXMLEXPORT_TEST(testTdf65955_2, "tdf65955_2.odt")
DECLARE_OOXMLEXPORT_TEST(testChtOutlineNumberingOoxml, "chtoutline.docx")
{
const sal_Unicode aExpectedPrefix[2] = { 0x7b2c, 0x0020 };
const sal_Unicode aExpectedSuffix[2] = { 0x0020, 0x7ae0 };
uno::Reference< text::XChapterNumberingSupplier > xChapterNumberingSupplier(mxComponent, uno::UNO_QUERY);
uno::Reference< container::XIndexAccess> xLevels(xChapterNumberingSupplier->getChapterNumberingRules());
uno::Sequence<beans::PropertyValue> aProps;
xLevels->getByIndex(0) >>= aProps; // 1st level
const sal_Unicode aExpectedNumbering[] = { 0x7b2c, ' ', '1', ' ', 0x7ae0 };
OUString aSuffix,aPrefix;
for (int i = 0; i < aProps.getLength(); ++i)
{
const beans::PropertyValue& rProp = aProps[i];
if (rProp.Name == "Suffix")
aSuffix = rProp.Value.get<OUString>();
if (rProp.Name == "Prefix")
aPrefix = rProp.Value.get<OUString>();
}
CPPUNIT_ASSERT_EQUAL(OUString(aExpectedPrefix,SAL_N_ELEMENTS(aExpectedPrefix)), aPrefix);
CPPUNIT_ASSERT_EQUAL(OUString(aExpectedSuffix,SAL_N_ELEMENTS(aExpectedSuffix)), aSuffix);
uno::Reference<beans::XPropertySet> xPara(getParagraph(1), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(OUString(aExpectedNumbering,SAL_N_ELEMENTS(aExpectedNumbering)),
getProperty<OUString>(xPara, "ListLabelString"));
}
DECLARE_OOXMLEXPORT_TEST(mathtype, "mathtype.docx")
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx
index 908faf1..f51804e 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx
@@ -995,6 +995,50 @@ DECLARE_OOXMLEXPORT_TEST(testTdf122594, "tdf122594.docx")
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(3), nCheck);
}
DECLARE_OOXMLEXPORT_TEST(testTdf116883, "tdf116883.docx")
{
{
uno::Reference<beans::XPropertySet> xPara(getParagraph(1), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(OUString("1>1>"), getProperty<OUString>(xPara, "ListLabelString"));
}
{
uno::Reference<beans::XPropertySet> xPara(getParagraph(2), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(OUString("1>2>"), getProperty<OUString>(xPara, "ListLabelString"));
}
{
uno::Reference<beans::XPropertySet> xPara(getParagraph(3), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(OUString("1>2>1>1>"), getProperty<OUString>(xPara, "ListLabelString"));
}
{
uno::Reference<beans::XPropertySet> xPara(getParagraph(4), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(OUString("1>2>2>"), getProperty<OUString>(xPara, "ListLabelString"));
}
{
uno::Reference<beans::XPropertySet> xPara(getParagraph(5), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(OUString("1>2>3>"), getProperty<OUString>(xPara, "ListLabelString"));
}
{
uno::Reference<beans::XPropertySet> xPara(getParagraph(6), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(OUString("1>1)"), getProperty<OUString>(xPara, "ListLabelString"));
}
{
uno::Reference<beans::XPropertySet> xPara(getParagraph(7), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(OUString("1>2)"), getProperty<OUString>(xPara, "ListLabelString"));
}
{
uno::Reference<beans::XPropertySet> xPara(getParagraph(8), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(OUString("1>2>1<1)"), getProperty<OUString>(xPara, "ListLabelString"));
}
{
uno::Reference<beans::XPropertySet> xPara(getParagraph(9), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(OUString("1>2.2)"), getProperty<OUString>(xPara, "ListLabelString"));
}
{
uno::Reference<beans::XPropertySet> xPara(getParagraph(10), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(OUString("1>2.3)"), getProperty<OUString>(xPara, "ListLabelString"));
}
}
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/extras/rtfexport/rtfexport.cxx b/sw/qa/extras/rtfexport/rtfexport.cxx
index cf2cb78..535374d 100644
--- a/sw/qa/extras/rtfexport/rtfexport.cxx
+++ b/sw/qa/extras/rtfexport/rtfexport.cxx
@@ -659,16 +659,16 @@ DECLARE_RTFEXPORT_TEST(testFdo66682, "fdo66682.rtf")
uno::Sequence<beans::PropertyValue> aProps;
xLevels->getByIndex(0) >>= aProps; // 1st level
OUString aSuffix;
OUString aListFormat;
for (int i = 0; i < aProps.getLength(); ++i)
{
const beans::PropertyValue& rProp = aProps[i];
if (rProp.Name == "Suffix")
aSuffix = rProp.Value.get<OUString>();
if (rProp.Name == "ListFormat")
aListFormat = rProp.Value.get<OUString>();
}
// Suffix was '\0' instead of ' '.
CPPUNIT_ASSERT_EQUAL(OUString(" "), aSuffix);
CPPUNIT_ASSERT_EQUAL(OUString(" %1 "), aListFormat);
}
DECLARE_RTFEXPORT_TEST(testParaShadow, "para-shadow.rtf")
diff --git a/sw/qa/extras/rtfimport/rtfimport.cxx b/sw/qa/extras/rtfimport/rtfimport.cxx
index a652b35..3fe9003 100644
--- a/sw/qa/extras/rtfimport/rtfimport.cxx
+++ b/sw/qa/extras/rtfimport/rtfimport.cxx
@@ -141,21 +141,52 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf108943)
CPPUNIT_TEST_FIXTURE(Test, testFdo46662)
{
load(mpTestDocumentPath, "fdo46662.rtf");
uno::Reference<beans::XPropertySet> xPropertySet(
getStyles("NumberingStyles")->getByName("WWNum3"), uno::UNO_QUERY);
uno::Reference<container::XIndexAccess> xLevels(
xPropertySet->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
uno::Sequence<beans::PropertyValue> aProps;
xLevels->getByIndex(1) >>= aProps; // 2nd level
for (int i = 0; i < aProps.getLength(); ++i)
OUString listStyle;
{
const beans::PropertyValue& rProp = aProps[i];
uno::Reference<beans::XPropertySet> xPara(getParagraph(1), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(0),
getProperty<sal_Int16>(xPara, "NumberingLevel"));
CPPUNIT_ASSERT(xPara->getPropertyValue("NumberingStyleName") >>= listStyle);
CPPUNIT_ASSERT(listStyle.startsWith("WWNum"));
CPPUNIT_ASSERT_EQUAL(OUString("1"), getProperty<OUString>(xPara, "ListLabelString"));
}
if (rProp.Name == "ParentNumbering")
CPPUNIT_ASSERT_EQUAL(sal_Int16(2), rProp.Value.get<sal_Int16>());
else if (rProp.Name == "Suffix")
CPPUNIT_ASSERT_EQUAL(sal_Int32(0), rProp.Value.get<OUString>().getLength());
{
uno::Reference<beans::XPropertySet> xPara(getParagraph(2), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(1),
getProperty<sal_Int16>(xPara, "NumberingLevel"));
CPPUNIT_ASSERT(xPara->getPropertyValue("NumberingStyleName") >>= listStyle);
CPPUNIT_ASSERT(listStyle.startsWith("WWNum"));
CPPUNIT_ASSERT_EQUAL(OUString("1.1"), getProperty<OUString>(xPara, "ListLabelString"));
}
{
uno::Reference<beans::XPropertySet> xPara(getParagraph(3), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(2),
getProperty<sal_Int16>(xPara, "NumberingLevel"));
CPPUNIT_ASSERT(xPara->getPropertyValue("NumberingStyleName") >>= listStyle);
CPPUNIT_ASSERT(listStyle.startsWith("WWNum"));
CPPUNIT_ASSERT_EQUAL(OUString("1.1.1"), getProperty<OUString>(xPara, "ListLabelString"));
}
{
uno::Reference<beans::XPropertySet> xPara(getParagraph(4), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(3),
getProperty<sal_Int16>(xPara, "NumberingLevel"));
CPPUNIT_ASSERT(xPara->getPropertyValue("NumberingStyleName") >>= listStyle);
CPPUNIT_ASSERT(listStyle.startsWith("WWNum"));
CPPUNIT_ASSERT_EQUAL(OUString("1.1.1.1"), getProperty<OUString>(xPara, "ListLabelString"));
}
{
uno::Reference<beans::XPropertySet> xPara(getParagraph(5), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int16>(0),
getProperty<sal_Int16>(xPara, "NumberingLevel"));
CPPUNIT_ASSERT(xPara->getPropertyValue("NumberingStyleName") >>= listStyle);
CPPUNIT_ASSERT(listStyle.startsWith("WWNum"));
CPPUNIT_ASSERT_EQUAL(OUString("2"), getProperty<OUString>(xPara, "ListLabelString"));
}
}
@@ -1037,25 +1068,12 @@ CPPUNIT_TEST_FIXTURE(Test, testCp950listleveltext1)
{
load(mpTestDocumentPath, "cp950listleveltext1.rtf");
// suffix with Chinese only ( most common case generated by MSO2010 TC)
const sal_Unicode aExpectedSuffix[1]
= { 0x3001 }; // This is a dot that is generally used as suffix of Chinese list number
uno::Reference<beans::XPropertySet> xPropertySet(
getStyles("NumberingStyles")->getByName("WWNum3"), uno::UNO_QUERY);
uno::Reference<container::XIndexAccess> xLevels(
xPropertySet->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
uno::Sequence<beans::PropertyValue> aProps;
xLevels->getByIndex(0) >>= aProps; // 1st level
// This is a dot that is generally used as suffix of Chinese list number
const sal_Unicode aExpectedNumbering[] = { 0x4e00, 0x3001 };
OUString aSuffix;
for (int i = 0; i < aProps.getLength(); ++i)
{
const beans::PropertyValue& rProp = aProps[i];
if (rProp.Name == "Suffix")
aSuffix = rProp.Value.get<OUString>();
}
// Suffix was '\0' instead of ' '.
CPPUNIT_ASSERT_EQUAL(OUString(aExpectedSuffix, SAL_N_ELEMENTS(aExpectedSuffix)), aSuffix);
uno::Reference<beans::XPropertySet> xPara(getParagraph(1), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(OUString(aExpectedNumbering, SAL_N_ELEMENTS(aExpectedNumbering)),
getProperty<OUString>(xPara, "ListLabelString"));
}
// This testcase illustrate leveltext with multibyte strings coded in cp950 ( BIG5 ).
@@ -1063,85 +1081,35 @@ CPPUNIT_TEST_FIXTURE(Test, testCp950listleveltext2)
{
load(mpTestDocumentPath, "cp950listleveltext2.rtf");
// Prefix and suffix with Chinese only ( tweaked from default in MSO2010 TC)
const sal_Unicode aExpectedPrefix[2] = { 0x524d, 0x7f6e };
const sal_Unicode aExpectedSuffix[3] = { 0x3001, 0x5f8c, 0x7f6e };
const sal_Unicode aExpectedNumbering[] = { 0x524d, 0x7f6e, 0x7532, 0x3001, 0x5f8c, 0x7f6e };
uno::Reference<beans::XPropertySet> xPropertySet(
getStyles("NumberingStyles")->getByName("WWNum1"), uno::UNO_QUERY);
uno::Reference<container::XIndexAccess> xLevels(
xPropertySet->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
uno::Sequence<beans::PropertyValue> aProps;
xLevels->getByIndex(0) >>= aProps; // 1st level
OUString aSuffix, aPrefix;
for (int i = 0; i < aProps.getLength(); ++i)
{
const beans::PropertyValue& rProp = aProps[i];
if (rProp.Name == "Suffix")
aSuffix = rProp.Value.get<OUString>();
if (rProp.Name == "Prefix")
aPrefix = rProp.Value.get<OUString>();
}
// Suffix was '\0' instead of ' '.
CPPUNIT_ASSERT_EQUAL(OUString(aExpectedPrefix, SAL_N_ELEMENTS(aExpectedPrefix)), aPrefix);
CPPUNIT_ASSERT_EQUAL(OUString(aExpectedSuffix, SAL_N_ELEMENTS(aExpectedSuffix)), aSuffix);
uno::Reference<beans::XPropertySet> xPara(getParagraph(1), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(OUString(aExpectedNumbering, SAL_N_ELEMENTS(aExpectedNumbering)),
getProperty<OUString>(xPara, "ListLabelString"));
}
// This testcase illustrate leveltext with multibyte strings coded in cp950 ( BIG5 )
CPPUNIT_TEST_FIXTURE(Test, testCp950listleveltext3)
{
load(mpTestDocumentPath, "cp950listleveltext3.rtf");
// Prefix and suffix that mix Chinese and English ( tweaked from default in MSO2010 TC)
const sal_Unicode aExpectedPrefix[4] = { 0x524d, 0x0061, 0x7f6e, 0x0062 };
const sal_Unicode aExpectedSuffix[6] = { 0x3001, 0x0063, 0x5f8c, 0x0064, 0x7f6e, 0x0065 };
// Numbering is a mix Chinese and English ( tweaked from default in MSO2010 TC)
const sal_Unicode aExpectedNumbering[] = { 0x524d, 0x0061, 0x7f6e, 0x0062, 0x7532, 0x3001,
0x0063, 0x5f8c, 0x0064, 0x7f6e, 0x0065 };
uno::Reference<beans::XPropertySet> xPropertySet(
getStyles("NumberingStyles")->getByName("WWNum1"), uno::UNO_QUERY);
uno::Reference<container::XIndexAccess> xLevels(
xPropertySet->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
uno::Sequence<beans::PropertyValue> aProps;
xLevels->getByIndex(0) >>= aProps; // 1st level
OUString aSuffix, aPrefix;
for (int i = 0; i < aProps.getLength(); ++i)
{
const beans::PropertyValue& rProp = aProps[i];
if (rProp.Name == "Suffix")
aSuffix = rProp.Value.get<OUString>();
if (rProp.Name == "Prefix")
aPrefix = rProp.Value.get<OUString>();
}
// Suffix was '\0' instead of ' '.
CPPUNIT_ASSERT_EQUAL(OUString(aExpectedPrefix, SAL_N_ELEMENTS(aExpectedPrefix)), aPrefix);
CPPUNIT_ASSERT_EQUAL(OUString(aExpectedSuffix, SAL_N_ELEMENTS(aExpectedSuffix)), aSuffix);
uno::Reference<beans::XPropertySet> xPara(getParagraph(1), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(OUString(aExpectedNumbering, SAL_N_ELEMENTS(aExpectedNumbering)),
getProperty<OUString>(xPara, "ListLabelString"));
}
CPPUNIT_TEST_FIXTURE(Test, testChtOutlineNumberingRtf)
{
load(mpTestDocumentPath, "chtoutline.rtf");
const sal_Unicode aExpectedPrefix[2] = { 0x7b2c, 0x0020 };
const sal_Unicode aExpectedSuffix[2] = { 0x0020, 0x7ae0 };
uno::Reference<text::XChapterNumberingSupplier> xChapterNumberingSupplier(mxComponent,
uno::UNO_QUERY);
uno::Reference<container::XIndexAccess> xLevels(
xChapterNumberingSupplier->getChapterNumberingRules());
uno::Sequence<beans::PropertyValue> aProps;
xLevels->getByIndex(0) >>= aProps; // 1st level
OUString aSuffix, aPrefix;
for (int i = 0; i < aProps.getLength(); ++i)
{
const beans::PropertyValue& rProp = aProps[i];
const sal_Unicode aExpectedNumbering[] = { 0x7b2c, ' ', '1', ' ', 0x7ae0 };
if (rProp.Name == "Suffix")
aSuffix = rProp.Value.get<OUString>();
if (rProp.Name == "Prefix")
aPrefix = rProp.Value.get<OUString>();
}
CPPUNIT_ASSERT_EQUAL(OUString(aExpectedPrefix, SAL_N_ELEMENTS(aExpectedPrefix)), aPrefix);
CPPUNIT_ASSERT_EQUAL(OUString(aExpectedSuffix, SAL_N_ELEMENTS(aExpectedSuffix)), aSuffix);
uno::Reference<beans::XPropertySet> xPara(getParagraph(1), uno::UNO_QUERY);
CPPUNIT_ASSERT_EQUAL(OUString(aExpectedNumbering, SAL_N_ELEMENTS(aExpectedNumbering)),
getProperty<OUString>(xPara, "ListLabelString"));
}
CPPUNIT_TEST_FIXTURE(Test, testTdf90046)
diff --git a/sw/source/core/doc/number.cxx b/sw/source/core/doc/number.cxx
index f579334..df3dcf7 100644
--- a/sw/source/core/doc/number.cxx
+++ b/sw/source/core/doc/number.cxx
@@ -639,64 +639,94 @@ OUString SwNumRule::MakeNumString( const SwNumberTree::tNumberVector & rNumVecto
const SwNumFormat& rMyNFormat = Get( static_cast<sal_uInt16>(nLevel) );
{
SwNumberTree::tNumberVector::size_type i = nLevel;
if( !IsContinusNum() &&
// - do not include upper levels, if level isn't numbered.
rMyNFormat.GetNumberingType() != SVX_NUM_NUMBER_NONE &&
rMyNFormat.GetIncludeUpperLevels() ) // Just the own level?
{
sal_uInt8 n = rMyNFormat.GetIncludeUpperLevels();
if( 1 < n )
{
if( i+1 >= n )
i -= n - 1;
else
i = 0;
}
}
css::lang::Locale aLocale( LanguageTag::convertToLocale(nLang));
for( ; i <= nLevel; ++i )
OUString sLevelFormat = rMyNFormat.GetListFormat();
if (!sLevelFormat.isEmpty())
{
const SwNumFormat& rNFormat = Get( i );
if( SVX_NUM_NUMBER_NONE == rNFormat.GetNumberingType() )
// In this case we are ignoring GetIncludeUpperLevels: we put all
// level nubers requested by level format
for (SwNumberTree::tNumberVector::size_type i=0; i <= nLevel; ++i)
{
// Should 1.1.1 --> 2. NoNum --> 1..1 or 1.1 ??
// if( i != rNum.nMyLevel )
// aStr += ".";
continue;
}
if( rNumVector[ i ] )
{
if( bOnlyArabic )
aStr.append(OUString::number( rNumVector[ i ] ));
OUString sReplacement;
if (rNumVector[i])
{
if (bOnlyArabic)
sReplacement = OUString::number(rNumVector[i]);
else
sReplacement = Get(i).GetNumStr(rNumVector[i], aLocale);
}
else
aStr.append(rNFormat.GetNumStr( rNumVector[ i ], aLocale ));
sReplacement = "0"; // all 0 level are a 0
OUString sFind("%" + OUString::number(i + 1));
sal_Int32 nPosition = sLevelFormat.indexOf(sFind);
if (nPosition >= 0)
sLevelFormat = sLevelFormat.replaceAt(nPosition, sFind.getLength(), sReplacement);
}
else
aStr.append("0"); // all 0 level are a 0
if( i != nLevel && !aStr.isEmpty() )
aStr.append(".");
aStr = sLevelFormat;
}
// The type doesn't have any number, so don't append
// the post-/prefix string
if( bInclStrings && !bOnlyArabic &&
SVX_NUM_CHAR_SPECIAL != rMyNFormat.GetNumberingType() &&
SVX_NUM_BITMAP != rMyNFormat.GetNumberingType() )
else
{
const OUString& sPrefix = rMyNFormat.GetPrefix();
const OUString& sSuffix = rMyNFormat.GetSuffix();
// Fallback case: level format is not defined
// So use old way with levels joining by dot "."
SwNumberTree::tNumberVector::size_type i = nLevel;
aStr.insert(0, sPrefix);
aStr.append(sSuffix);
if ( pExtremities )
if (!IsContinusNum() &&
// - do not include upper levels, if level isn't numbered.
rMyNFormat.GetNumberingType() != SVX_NUM_NUMBER_NONE &&
rMyNFormat.GetIncludeUpperLevels()) // Just the own level?
{
pExtremities->nPrefixChars = sPrefix.getLength();
pExtremities->nSuffixChars = sSuffix.getLength();
sal_uInt8 n = rMyNFormat.GetIncludeUpperLevels();
if (1 < n)
{
if (i + 1 >= n)
i -= n - 1;
else
i = 0;
}
}
for (; i <= nLevel; ++i)
{
const SwNumFormat& rNFormat = Get(i);
if (SVX_NUM_NUMBER_NONE == rNFormat.GetNumberingType())
{
// Should 1.1.1 --> 2. NoNum --> 1..1 or 1.1 ??
// if( i != rNum.nMyLevel )
// aStr += ".";
continue;
}
if (rNumVector[i])
{
if (bOnlyArabic)
aStr.append(OUString::number(rNumVector[i]));
else
aStr.append(rNFormat.GetNumStr(rNumVector[i], aLocale));
}
else
aStr.append("0"); // all 0 level are a 0
if (i != nLevel && !aStr.isEmpty())
aStr.append(".");
}
// The type doesn't have any number, so don't append
// the post-/prefix string
if (bInclStrings && !bOnlyArabic &&
SVX_NUM_CHAR_SPECIAL != rMyNFormat.GetNumberingType() &&
SVX_NUM_BITMAP != rMyNFormat.GetNumberingType())
{
const OUString& sPrefix = rMyNFormat.GetPrefix();
const OUString& sSuffix = rMyNFormat.GetSuffix();
aStr.insert(0, sPrefix);
aStr.append(sSuffix);
if (pExtremities)
{
pExtremities->nPrefixChars = sPrefix.getLength();
pExtremities->nSuffixChars = sSuffix.getLength();
}
}
}
}
diff --git a/sw/source/core/unocore/unosett.cxx b/sw/source/core/unocore/unosett.cxx
index c41d13d..2b3b08e 100644
--- a/sw/source/core/unocore/unosett.cxx
+++ b/sw/source/core/unocore/unosett.cxx
@@ -1342,6 +1342,10 @@ uno::Sequence<beans::PropertyValue> SwXNumberingRules::GetPropertiesForNumFormat
aUString = rFormat.GetSuffix();
aPropertyValues.push_back(comphelper::makePropertyValue("Suffix", aUString));
//listformat
aUString = rFormat.GetListFormat();
aPropertyValues.push_back(comphelper::makePropertyValue("ListFormat", aUString));
//char style name
aUString.clear();
@@ -1581,7 +1585,9 @@ void SwXNumberingRules::SetPropertiesToNumFormat(
// these two are accepted but ignored for some reason
UNO_NAME_BULLET_REL_SIZE, // 25
UNO_NAME_BULLET_COLOR, // 26
UNO_NAME_GRAPHIC_URL // 27
UNO_NAME_GRAPHIC_URL, // 27
UNO_NAME_LIST_FORMAT // 28
};
enum {
@@ -2035,6 +2041,13 @@ void SwXNumberingRules::SetPropertiesToNumFormat(
bWrongArg = true;
}
break;
case 28: //"ListFormat",
{
OUString uTmp;
pProp->Value >>= uTmp;
aFormat.SetListFormat(uTmp);
}
break;
}
}
if(!bWrongArg && (pSetBrush || pSetSize || pSetVOrient))
diff --git a/sw/source/filter/ww8/wrtw8num.cxx b/sw/source/filter/ww8/wrtw8num.cxx
index e665a25..afe0204 100644
--- a/sw/source/filter/ww8/wrtw8num.cxx
+++ b/sw/source/filter/ww8/wrtw8num.cxx
@@ -489,7 +489,13 @@ void MSWordExportBase::NumberingLevel(
const vcl::Font* pBulletFont=nullptr;
rtl_TextEncoding eChrSet=0;
FontFamily eFamily=FAMILY_DECORATIVE;
if (SVX_NUM_CHAR_SPECIAL == rFormat.GetNumberingType() ||
if (!rRule.Get(nLvl).GetListFormat().isEmpty())
{
// We have stored list format, use it
sNumStr = rRule.Get(nLvl).GetListFormat();
}
else if (SVX_NUM_CHAR_SPECIAL == rFormat.GetNumberingType() ||
SVX_NUM_BITMAP == rFormat.GetNumberingType())
{
sNumStr = OUString(rFormat.GetBulletChar());
diff --git a/writerfilter/source/dmapper/NumberingManager.cxx b/writerfilter/source/dmapper/NumberingManager.cxx
index 32dead6..eab1ac8 100644
--- a/writerfilter/source/dmapper/NumberingManager.cxx
+++ b/writerfilter/source/dmapper/NumberingManager.cxx
@@ -42,6 +42,7 @@
#include <tools/diagnose_ex.h>
#include <comphelper/sequence.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/string.hxx>
using namespace com::sun::star;
@@ -149,55 +150,6 @@ void ListLevel::SetParaStyle( const tools::SvRef< StyleSheetEntry >& pStyle )
&& styleId[ RTL_CONSTASCII_LENGTH( "Heading " ) ] <= '9' );
}
sal_Int16 ListLevel::GetParentNumbering( const OUString& sText, sal_Int16 nLevel,
OUString& rPrefix, OUString& rSuffix )
{
sal_Int16 nParentNumbering = 1;
//now parse the text to find %n from %1 to %nLevel+1
//everything before the first % and the last %x is prefix and suffix
OUString sLevelText( sText );
sal_Int32 nCurrentIndex = 0;
sal_Int32 nFound = sLevelText.indexOf( '%', nCurrentIndex );
if( nFound > 0 )
{
rPrefix = sLevelText.copy( 0, nFound );
sLevelText = sLevelText.copy( nFound );
}
sal_Int32 nMinLevel = nLevel;
//now the text should either be empty or start with %
nFound = sLevelText.getLength( ) > 1 ? 0 : -1;
while( nFound >= 0 )
{
if( sLevelText.getLength() > 1 )
{
sal_Unicode cLevel = sLevelText[1];
if( cLevel >= '1' && cLevel <= '9' )
{
if( cLevel - '1' < nMinLevel )
nMinLevel = cLevel - '1';
//remove first char - next char is removed later
sLevelText = sLevelText.copy( 1 );
}
}
//remove old '%' or number
sLevelText = sLevelText.copy( 1 );
nCurrentIndex = 0;
nFound = sLevelText.indexOf( '%', nCurrentIndex );
//remove the text before the next %
if(nFound > 0)
sLevelText = sLevelText.copy( nFound -1 );
}
if( nMinLevel < nLevel )
{
nParentNumbering = sal_Int16( nLevel - nMinLevel + 1);
}
rSuffix = sLevelText;
return nParentNumbering;
}
uno::Sequence<beans::PropertyValue> ListLevel::GetProperties(bool bDefaults)
{
uno::Sequence<beans::PropertyValue> aLevelProps = GetLevelProperties(bDefaults);
@@ -588,19 +540,11 @@ void ListDef::CreateNumberingRules( DomainMapper& rDMapper,
if (pLevel.get() && !pLevel->GetBulletChar().isEmpty())
sText = pLevel->GetBulletChar( );
OUString sPrefix;
OUString sSuffix;
OUString& rPrefix = sPrefix;
OUString& rSuffix = sSuffix;
sal_Int16 nParentNum = ListLevel::GetParentNumbering(
sText, nLevel, rPrefix, rSuffix );
aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_PREFIX), rPrefix));
if (sText.isEmpty())
{
// Empty <w:lvlText>? Then put a Unicode "zero width space" as a suffix, so LabelFollowedBy is still shown, as in Word.
// With empty suffix, Writer does not show LabelFollowedBy, either.
OUString sSuffix;
auto it = std::find_if(aLvlProps.begin(), aLvlProps.end(), [](const beans::PropertyValue& rValue) { return rValue.Name == "NumberingType"; });
if (it != aLvlProps.end())
{
@@ -617,12 +561,19 @@ void ListDef::CreateNumberingRules( DomainMapper& rDMapper,
}
if (bLabelFollowedBy && nNumberFormat == style::NumberingType::NUMBER_NONE)
rSuffix = OUString(u'\x200B');
sSuffix = OUString(u'\x200B');
}
aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SUFFIX), sSuffix));
}
else
{
aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_LIST_FORMAT), sText));
aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SUFFIX), rSuffix));
aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_PARENT_NUMBERING), nParentNum));
// Total count of replacement holders is determining amount of required parent numbering to include
// TODO: not sure how "%" symbol is escaped. This is not supported yet
sal_Int16 nParentNum = comphelper::string::getTokenCount(sText, '%');
aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_PARENT_NUMBERING), nParentNum));
}
aLvlProps.push_back(comphelper::makePropertyValue(getPropertyName(PROP_POSITION_AND_SPACE_MODE), sal_Int16(text::PositionAndSpaceMode::LABEL_ALIGNMENT)));
diff --git a/writerfilter/source/dmapper/NumberingManager.hxx b/writerfilter/source/dmapper/NumberingManager.hxx
index c0e68d9..3949c3b 100644
--- a/writerfilter/source/dmapper/NumberingManager.hxx
+++ b/writerfilter/source/dmapper/NumberingManager.hxx
@@ -86,11 +86,6 @@ public:
bool HasValues() const;
// UNO mapping functions
// rPrefix and rSuffix are out parameters
static sal_Int16 GetParentNumbering( const OUString& sText, sal_Int16 nLevel,
OUString& rPrefix, OUString& rSuffix );
css::uno::Sequence<css::beans::PropertyValue> GetProperties(bool bDefaults);
css::uno::Sequence<css::beans::PropertyValue> GetCharStyleProperties();
diff --git a/writerfilter/source/dmapper/PropertyIds.cxx b/writerfilter/source/dmapper/PropertyIds.cxx
index 0d5740e9..6fecf7f 100644
--- a/writerfilter/source/dmapper/PropertyIds.cxx
+++ b/writerfilter/source/dmapper/PropertyIds.cxx
@@ -207,6 +207,7 @@ OUString getPropertyName( PropertyIds eId )
case PROP_LEVEL_FOLLOW : sName = "LabelFollowedBy"; break;
case PROP_LEVEL_PARAGRAPH_STYLES : sName = "LevelParagraphStyles"; break;
case PROP_LEVEL_FORMAT : sName = "LevelFormat"; break;
case PROP_LIST_FORMAT : sName = "ListFormat"; break;
case PROP_TOKEN_TYPE : sName = "TokenType"; break;
case PROP_TOKEN_HYPERLINK_START : sName = "TokenHyperlinkStart"; break;
case PROP_TOKEN_HYPERLINK_END : sName = "TokenHyperlinkEnd"; break;
diff --git a/writerfilter/source/dmapper/PropertyIds.hxx b/writerfilter/source/dmapper/PropertyIds.hxx
index a1a0c8f..d511fd7 100644
--- a/writerfilter/source/dmapper/PropertyIds.hxx
+++ b/writerfilter/source/dmapper/PropertyIds.hxx
@@ -205,6 +205,7 @@ enum PropertyIds
,PROP_LEVEL_FORMAT
,PROP_LEVEL_PARAGRAPH_STYLES
,PROP_LISTTAB_STOP_POSITION
,PROP_LIST_FORMAT
,PROP_MACRO_NAME
,PROP_NAME
,PROP_NUMBERING_LEVEL