sw/qa/extras/rtfexport/data/tdf167661.rtf | 12 ++ sw/qa/extras/rtfexport/data/tdf167679.fodt | 25 +++++ sw/qa/extras/rtfexport/rtfexport8.cxx | 128 +++++++++++++++++++++++++++++ sw/source/filter/ww8/wrtw8num.cxx | 105 +++++++++++------------ sw/source/filter/ww8/wrtw8sty.cxx | 43 ++++++--- sw/source/filter/ww8/wrtww8.cxx | 2 sw/source/filter/ww8/wrtww8.hxx | 6 + sw/source/filter/ww8/ww8atr.cxx | 16 +-- 8 files changed, 257 insertions(+), 80 deletions(-)
New commits: commit 1749f616197fc10100ceacd974b4e45ce8a02bcd Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Sat Jul 26 21:33:06 2025 +0500 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Mon Aug 11 12:12:35 2025 +0200 tdf#167679: RTF already can export rdrnone In commit f84b33275f6cce21e93e5dd20f3de5df84df0276 (tdf#129522 ww8import/export: allow char shadow_NONE overrides, 2020-01-04), export of char shadow was implemented, which made CharBorder to be called for missing borderline. But it made an exception for RTF, because at that time, its code couldn't handle that. In commit eca3ce35fe9a346965a32f42d02cb6d3f5a3982f (tdf#129631 writerfilter,sw: RTF import of invalid border..., 2022-08-11), RTF export got ability to handle that. Removal of the exception for RTF fixed missing export of "no border" and "no shadow" character formatting. Change-Id: I5f23b09558d32b403e0c26c727ee01f79374e54d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188418 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189304 diff --git a/sw/qa/extras/rtfexport/data/tdf167679.fodt b/sw/qa/extras/rtfexport/data/tdf167679.fodt new file mode 100644 index 000000000000..fad1dacf5b45 --- /dev/null +++ b/sw/qa/extras/rtfexport/data/tdf167679.fodt @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible: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:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:font-face-decls> + <style:font-face style:name="Liberation Serif" svg:font-family="'Liberation Serif'" style:font-family-generic="roman" style:font-pitch="variable"/> + </office:font-face-decls> + <office:styles> + <style:default-style style:family="paragraph"> + <style:text-properties style:font-name="Liberation Serif" fo:font-size="12pt"/> + </style:default-style> + <style:style style:name="border" style:family="text"> + <style:text-properties loext:padding="0.5mm" loext:border="0.11pt solid #FF0000" loext:shadow="#808080 5pt 5pt"/> + </style:style> + </office:styles> + <office:automatic-styles> + <style:style style:name="T1" style:family="text"> + <style:text-properties loext:padding="0.5mm" loext:border="none" loext:shadow="none"/> + </style:style> + </office:automatic-styles> + <office:body> + <office:text> + <text:p>s<text:span text:style-name="border">om<text:span text:style-name="T1">eth</text:span>in</text:span>g</text:p> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/sw/qa/extras/rtfexport/rtfexport8.cxx b/sw/qa/extras/rtfexport/rtfexport8.cxx index d03dd3fbfe3a..c9b641affc16 100644 --- a/sw/qa/extras/rtfexport/rtfexport8.cxx +++ b/sw/qa/extras/rtfexport/rtfexport8.cxx @@ -23,6 +23,7 @@ #include <com/sun/star/text/XTextDocument.hpp> #include <com/sun/star/style/ParagraphAdjust.hpp> #include <com/sun/star/style/TabStop.hpp> +#include <com/sun/star/table/ShadowFormat.hpp> #include <basegfx/utils/gradienttools.hxx> #include <comphelper/sequenceashashmap.hxx> @@ -1046,6 +1047,100 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf167661) } } +CPPUNIT_TEST_FIXTURE(Test, testTdf167679) +{ + // Given a document with a char style with a border with a shadow, and a direct formatting + // applied over it, which turns off the border and the shadow: + createSwDoc("tdf167679.fodt"); + + { + auto xRun = getRun(getParagraph(1), 1, u"s"_ustr); + auto aBorder = getProperty<table::BorderLine2>(xRun, u"CharTopBorder"_ustr); + CPPUNIT_ASSERT_BORDER_EQUAL(table::BorderLine2(0x000000, 0, 0, 0, 32767, 0), aBorder); + + auto aShadow = getProperty<table::ShadowFormat>(xRun, u"CharShadowFormat"_ustr); + CPPUNIT_ASSERT_EQUAL(table::ShadowLocation_NONE, aShadow.Location); + } + { + auto xRun = getRun(getParagraph(1), 2, u"om"_ustr); + auto aBorder = getProperty<table::BorderLine2>(xRun, u"CharTopBorder"_ustr); + CPPUNIT_ASSERT_BORDER_EQUAL(table::BorderLine2(0xFF0000, 0, 4, 0, 0, 4), aBorder); + + auto aShadow = getProperty<table::ShadowFormat>(xRun, u"CharShadowFormat"_ustr); + CPPUNIT_ASSERT_EQUAL(table::ShadowLocation_BOTTOM_RIGHT, aShadow.Location); + } + { + auto xRun = getRun(getParagraph(1), 3, u"eth"_ustr); + auto aBorder = getProperty<table::BorderLine2>(xRun, u"CharTopBorder"_ustr); + CPPUNIT_ASSERT_BORDER_EQUAL(table::BorderLine2(0x000000, 0, 0, 0, 32767, 0), aBorder); + + auto aShadow = getProperty<table::ShadowFormat>(xRun, u"CharShadowFormat"_ustr); + CPPUNIT_ASSERT_EQUAL(table::ShadowLocation_NONE, aShadow.Location); + } + { + auto xRun = getRun(getParagraph(1), 4, u"in"_ustr); + auto aBorder = getProperty<table::BorderLine2>(xRun, u"CharTopBorder"_ustr); + CPPUNIT_ASSERT_BORDER_EQUAL(table::BorderLine2(0xFF0000, 0, 4, 0, 0, 4), aBorder); + + auto aShadow = getProperty<table::ShadowFormat>(xRun, u"CharShadowFormat"_ustr); + CPPUNIT_ASSERT_EQUAL(table::ShadowLocation_BOTTOM_RIGHT, aShadow.Location); + } + { + auto xRun = getRun(getParagraph(1), 5, u"g"_ustr); + auto aBorder = getProperty<table::BorderLine2>(xRun, u"CharTopBorder"_ustr); + CPPUNIT_ASSERT_BORDER_EQUAL(table::BorderLine2(0x000000, 0, 0, 0, 32767, 0), aBorder); + + auto aShadow = getProperty<table::ShadowFormat>(xRun, u"CharShadowFormat"_ustr); + CPPUNIT_ASSERT_EQUAL(table::ShadowLocation_NONE, aShadow.Location); + } + + // Check that after export to RTF, the area in the middle still has no border + saveAndReload(mpFilter); + + { + auto xRun = getRun(getParagraph(1), 1, u"s"_ustr); + auto aBorder = getProperty<table::BorderLine2>(xRun, u"CharTopBorder"_ustr); + CPPUNIT_ASSERT_BORDER_EQUAL(table::BorderLine2(0x000000, 0, 0, 0, 32767, 0), aBorder); + + auto aShadow = getProperty<table::ShadowFormat>(xRun, u"CharShadowFormat"_ustr); + CPPUNIT_ASSERT_EQUAL(table::ShadowLocation_NONE, aShadow.Location); + } + { + // Without a fix, this failed, because the second run was "omethin": the middle + // direct formatting that cancelled the border didn't round-trip. + auto xRun = getRun(getParagraph(1), 2, u"om"_ustr); + auto aBorder = getProperty<table::BorderLine2>(xRun, u"CharTopBorder"_ustr); + CPPUNIT_ASSERT_BORDER_EQUAL(table::BorderLine2(0xFF0000, 0, 4, 0, 0, 4), aBorder); + + auto aShadow = getProperty<table::ShadowFormat>(xRun, u"CharShadowFormat"_ustr); + CPPUNIT_ASSERT_EQUAL(table::ShadowLocation_BOTTOM_RIGHT, aShadow.Location); + } + { + auto xRun = getRun(getParagraph(1), 3, u"eth"_ustr); + auto aBorder = getProperty<table::BorderLine2>(xRun, u"CharTopBorder"_ustr); + CPPUNIT_ASSERT_BORDER_EQUAL(table::BorderLine2(0x000000, 0, 0, 0, 32767, 0), aBorder); + + auto aShadow = getProperty<table::ShadowFormat>(xRun, u"CharShadowFormat"_ustr); + CPPUNIT_ASSERT_EQUAL(table::ShadowLocation_NONE, aShadow.Location); + } + { + auto xRun = getRun(getParagraph(1), 4, u"in"_ustr); + auto aBorder = getProperty<table::BorderLine2>(xRun, u"CharTopBorder"_ustr); + CPPUNIT_ASSERT_BORDER_EQUAL(table::BorderLine2(0xFF0000, 0, 4, 0, 0, 4), aBorder); + + auto aShadow = getProperty<table::ShadowFormat>(xRun, u"CharShadowFormat"_ustr); + CPPUNIT_ASSERT_EQUAL(table::ShadowLocation_BOTTOM_RIGHT, aShadow.Location); + } + { + auto xRun = getRun(getParagraph(1), 5, u"g"_ustr); + auto aBorder = getProperty<table::BorderLine2>(xRun, u"CharTopBorder"_ustr); + CPPUNIT_ASSERT_BORDER_EQUAL(table::BorderLine2(0x000000, 0, 0, 0, 32767, 0), aBorder); + + auto aShadow = getProperty<table::ShadowFormat>(xRun, u"CharShadowFormat"_ustr); + CPPUNIT_ASSERT_EQUAL(table::ShadowLocation_NONE, aShadow.Location); + } +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/filter/ww8/ww8atr.cxx b/sw/source/filter/ww8/ww8atr.cxx index 878fe048b66f..8019d895db11 100644 --- a/sw/source/filter/ww8/ww8atr.cxx +++ b/sw/source/filter/ww8/ww8atr.cxx @@ -6076,17 +6076,13 @@ void AttributeOutputBase::FormatCharBorder( const SvxBoxItem& rBox ) nDist = rBox.GetDistance( SvxBoxItemLine::RIGHT ); } - // RTF: avoid regressions since RTF doesn't know how to export a border_NONE style-override - if( pBorderLine || GetExport().GetExportFormat() != MSWordExportBase::ExportFormat::RTF ) - { - const SfxPoolItem* pItem = GetExport().HasItem( RES_CHRATR_SHADOW ); - const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem); - const bool bShadow = pBorderLine && - pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE && - pShadowItem->GetWidth() > 0; + const SfxPoolItem* pItem = GetExport().HasItem( RES_CHRATR_SHADOW ); + const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem); + const bool bShadow = pBorderLine && + pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE && + pShadowItem->GetWidth() > 0; - CharBorder( pBorderLine, nDist, bShadow ); - } + CharBorder( pBorderLine, nDist, bShadow ); } /* commit 66a612ee153e64afd5fe7024555be960e56c0a89 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Thu Jul 24 15:35:13 2025 +0500 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Mon Aug 11 12:12:21 2025 +0200 tdf#167661: collect bullet fonts before exporting font table The problem was, that InitFontTable, used to collect all fonts used in the document, didn't handle bullets. When font table was written, some fonts needed for bullets could be still missing from it; only when writing list levels, these fonts were added to maFonts. It might seem that that is a bug in SwDoc::ForEachCharacterFontItem, called from InitFontTable. It might be; and maybe even needs to be addressed at some point (though I don't know which side effects it could have). But in the case of export to RTF, this wouldn't fix it completely, because there is some special processing of bullets in MSWordExportBase::SubstituteBullet, which might change the exported font name; therefore, it could happen to still be not covered by an iteration over actual document's fonts. 1. Extract the code filling used numbering rules in GetNumberingId, into a separate function (EnsureUsedNumberingTable). 2. Extract the code getting bullet string + font in NumberingLevel, into a separate function (GetNumberingLevelBulletStringAndFont). 3. In wwFontHelper::InitFontTable, use these functions to iterate the used numbering rules, and handle fonts of these. To do that, let the function take an Export object. Change-Id: Ib7ecdeb9908cd3bd53346ccb035d623fa603db0d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188277 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189303 diff --git a/sw/qa/extras/rtfexport/data/tdf167661.rtf b/sw/qa/extras/rtfexport/data/tdf167661.rtf new file mode 100644 index 000000000000..368c0c56a338 --- /dev/null +++ b/sw/qa/extras/rtfexport/data/tdf167661.rtf @@ -0,0 +1,12 @@ +{ tf1 +{onttbl +{0bidi romancharset0prq2 Liberation Sans;} +{1bidi nilcharset2prq2 Wingdings;}} +{\*\listtable +{\list\listtemplateid-1 +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u183 ?;}{\levelnumbers;}1 i-360\li720\lin720 } +{\listname ;}\listid1}} +{\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}} +\pard i-360\li720 i0\ls1 in0\lin720\itap00 bullet list item +\par +} \ No newline at end of file diff --git a/sw/qa/extras/rtfexport/rtfexport8.cxx b/sw/qa/extras/rtfexport/rtfexport8.cxx index 9c36b8596691..d03dd3fbfe3a 100644 --- a/sw/qa/extras/rtfexport/rtfexport8.cxx +++ b/sw/qa/extras/rtfexport/rtfexport8.cxx @@ -1013,6 +1013,39 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf167660) CPPUNIT_ASSERT(buffer_view.find("\u-1278") != std::string_view::npos); } +CPPUNIT_TEST_FIXTURE(Test, testTdf167661) +{ + // Given a document with a bulleted list using Wingdings + createSwDoc("tdf167661.rtf"); + + { + // Check font of the bullet + auto xNumberingRules = getProperty<uno::Reference<container::XIndexAccess>>( + getParagraph(1), u"NumberingRules"_ustr); + + comphelper::SequenceAsHashMap level1( + xNumberingRules->getByIndex(0).get<uno::Sequence<beans::PropertyValue>>()); + + CPPUNIT_ASSERT_EQUAL(u"Wingdings"_ustr, level1[u"BulletFontName"_ustr].get<OUString>()); + } + + saveAndReload(mpFilter); + + { + // Check that the font of the bullet is not lost + auto xNumberingRules = getProperty<uno::Reference<container::XIndexAccess>>( + getParagraph(1), u"NumberingRules"_ustr); + + comphelper::SequenceAsHashMap level1( + xNumberingRules->getByIndex(0).get<uno::Sequence<beans::PropertyValue>>()); + + // Without a fix, this failed with + // - Expected: Wingdings + // - Actual : 0 + CPPUNIT_ASSERT_EQUAL(u"Wingdings"_ustr, level1[u"BulletFontName"_ustr].get<OUString>()); + } +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/filter/ww8/wrtw8num.cxx b/sw/source/filter/ww8/wrtw8num.cxx index 6cac001caef5..58a027b5bf4a 100644 --- a/sw/source/filter/ww8/wrtw8num.cxx +++ b/sw/source/filter/ww8/wrtw8num.cxx @@ -117,7 +117,7 @@ void MSWordExportBase::AddListLevelOverride(sal_uInt16 nListId, m_ListLevelOverrides[nListId][nLevelNum] = nStartAt; } -sal_uInt16 MSWordExportBase::GetNumberingId( const SwNumRule& rNumRule ) +void MSWordExportBase::EnsureUsedNumberingTable() { if ( !m_pUsedNumTable ) { @@ -146,6 +146,11 @@ sal_uInt16 MSWordExportBase::GetNumberingId( const SwNumRule& rNumRule ) m_pUsedNumTable->push_back( pR ); } } +} + +sal_uInt16 MSWordExportBase::GetNumberingId( const SwNumRule& rNumRule ) +{ + EnsureUsedNumberingTable(); SwNumRule* p = const_cast<SwNumRule*>(&rNumRule); sal_uInt16 nRet = o3tl::narrowing<sal_uInt16>(m_pUsedNumTable->GetPos(p)); @@ -413,6 +418,35 @@ void MSWordExportBase::AbstractNumberingDefinitions() } } +std::pair<OUString, std::unique_ptr<wwFont>> +MSWordExportBase::GetNumberingLevelBulletStringAndFont(const SwNumFormat& rLevelFormat) +{ + assert(rLevelFormat.GetNumberingType() == SVX_NUM_CHAR_SPECIAL + || rLevelFormat.GetNumberingType() == SVX_NUM_BITMAP); + + sal_UCS4 cBullet = rLevelFormat.GetBulletChar(); + OUString sNumStr(&cBullet, 1); + + std::optional<vcl::Font> pBulletFont = rLevelFormat.GetBulletFont(); + if (!pBulletFont) + { + pBulletFont = numfunc::GetDefBulletFont(); + } + + rtl_TextEncoding eChrSet = pBulletFont->GetCharSet(); + OUString sFontName = pBulletFont->GetFamilyName(); + FontFamily eFamily = pBulletFont->GetFamilyTypeMaybeAskConfig(); + + if (IsOpenSymbol(sFontName)) + SubstituteBullet(sNumStr, eChrSet, sFontName); + + if (sFontName.isEmpty()) + sFontName = pBulletFont->GetFamilyName(); + + return { sNumStr, std::make_unique<wwFont>(sFontName, pBulletFont->GetPitchMaybeAskConfig(), + eFamily, eChrSet) }; +} + void MSWordExportBase::NumberingLevel( SwNumRule const& rRule, sal_uInt8 const nLvl) { @@ -462,17 +496,25 @@ void MSWordExportBase::NumberingLevel( // Build the NumString for this Level OUString sNumStr; - OUString sFontName; - bool bWriteBullet = false; - std::optional<vcl::Font> pBulletFont; - rtl_TextEncoding eChrSet=0; - FontFamily eFamily=FAMILY_DECORATIVE; + + // Attributes of the numbering + std::unique_ptr<wwFont> pPseudoFont; + const SfxItemSet* pOutSet = nullptr; + + // cbGrpprlChpx + SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END> aSet( m_rDoc.GetAttrPool() ); + if (SVX_NUM_CHAR_SPECIAL == rFormat.GetNumberingType() || SVX_NUM_BITMAP == rFormat.GetNumberingType()) { // Use bullet - sal_UCS4 cBullet = rFormat.GetBulletChar(); - sNumStr = OUString(&cBullet, 1); + std::tie(sNumStr, pPseudoFont) = GetNumberingLevelBulletStringAndFont(rFormat); + + pOutSet = &aSet; + if (rFormat.GetCharFormat()) + aSet.Put( rFormat.GetCharFormat()->GetAttrSet() ); + aSet.ClearItem( RES_CHRATR_CJK_FONT ); + aSet.ClearItem( RES_CHRATR_FONT ); } else { @@ -551,51 +593,8 @@ void MSWordExportBase::NumberingLevel( assert(false && "deprecated format still exists and is unhandled. Inform Vasily or Justin"); } - if (SVX_NUM_CHAR_SPECIAL == rFormat.GetNumberingType() || - SVX_NUM_BITMAP == rFormat.GetNumberingType()) - { - bWriteBullet = true; - - pBulletFont = rFormat.GetBulletFont(); - if (!pBulletFont) - { - pBulletFont = numfunc::GetDefBulletFont(); - } - - eChrSet = pBulletFont->GetCharSet(); - sFontName = pBulletFont->GetFamilyName(); - eFamily = pBulletFont->GetFamilyTypeMaybeAskConfig(); - - if (IsOpenSymbol(sFontName)) - SubstituteBullet(sNumStr, eChrSet, sFontName); - } - - // Attributes of the numbering - std::unique_ptr<wwFont> pPseudoFont; - const SfxItemSet* pOutSet = nullptr; - - // cbGrpprlChpx - SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END> aSet( m_rDoc.GetAttrPool() ); - if (rFormat.GetCharFormat() || bWriteBullet) - { - if (bWriteBullet) - { - pOutSet = &aSet; - - if (rFormat.GetCharFormat()) - aSet.Put( rFormat.GetCharFormat()->GetAttrSet() ); - aSet.ClearItem( RES_CHRATR_CJK_FONT ); - aSet.ClearItem( RES_CHRATR_FONT ); - - if (sFontName.isEmpty()) - sFontName = pBulletFont->GetFamilyName(); - - pPseudoFont.reset(new wwFont( sFontName, pBulletFont->GetPitchMaybeAskConfig(), - eFamily, eChrSet)); - } - else - pOutSet = &rFormat.GetCharFormat()->GetAttrSet(); - } + if (!pOutSet && rFormat.GetCharFormat()) + pOutSet = &rFormat.GetCharFormat()->GetAttrSet(); sal_Int16 nIndentAt = 0; sal_Int16 nFirstLineIndex = 0; diff --git a/sw/source/filter/ww8/wrtw8sty.cxx b/sw/source/filter/ww8/wrtw8sty.cxx index 79b8371714d0..6ba31b1ad55b 100644 --- a/sw/source/filter/ww8/wrtw8sty.cxx +++ b/sw/source/filter/ww8/wrtw8sty.cxx @@ -919,7 +919,7 @@ sal_uInt16 wwFontHelper::GetId(const wwFont &rFont) return nRet; } -void wwFontHelper::InitFontTable(const SwDoc& rDoc) +void wwFontHelper::InitFontTable(MSWordExportBase& rExport) { GetId(wwFont(u"Times New Roman", PITCH_VARIABLE, FAMILY_ROMAN, RTL_TEXTENCODING_MS_1252)); @@ -930,18 +930,10 @@ void wwFontHelper::InitFontTable(const SwDoc& rDoc) GetId(wwFont(u"Arial", PITCH_VARIABLE, FAMILY_SWISS, RTL_TEXTENCODING_MS_1252)); - const SvxFontItem* pFont = GetDfltAttr(RES_CHRATR_FONT); + GetId(*GetDfltAttr(RES_CHRATR_FONT)); - GetId(wwFont(pFont->GetFamilyName(), pFont->GetPitch(), - pFont->GetFamily(), pFont->GetCharSet())); - - const SfxItemPool& rPool = rDoc.GetAttrPool(); - pFont = rPool.GetUserDefaultItem(RES_CHRATR_FONT); - if (nullptr != pFont) - { - GetId(wwFont(pFont->GetFamilyName(), pFont->GetPitch(), - pFont->GetFamily(), pFont->GetCharSet())); - } + if (const SvxFontItem* pFont = rExport.m_rDoc.GetAttrPool().GetUserDefaultItem(RES_CHRATR_FONT)) + GetId(*pFont); if (!m_bLoadAllFonts) return; @@ -949,14 +941,35 @@ void wwFontHelper::InitFontTable(const SwDoc& rDoc) const TypedWhichId<SvxFontItem> aTypes[] { RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT }; for (const TypedWhichId<SvxFontItem> & pId : aTypes) { - const_cast<SwDoc&>(rDoc).ForEachCharacterFontItem(pId, /*bIgnoreAutoStyles*/false, + rExport.m_rDoc.ForEachCharacterFontItem(pId, /*bIgnoreAutoStyles*/false, [this] (const SvxFontItem& rFontItem) -> bool { - GetId(wwFont(rFontItem.GetFamilyName(), rFontItem.GetPitch(), - rFontItem.GetFamily(), rFontItem.GetCharSet())); + GetId(rFontItem); return true; }); } + + // Bullets in lists may need own fonts; and may even want to substitute fonts (see + // MSWordExportBase::SubstituteBullet). We need to collect these here, too. + rExport.EnsureUsedNumberingTable(); + for (const SwNumRule* pRule : *rExport.m_pUsedNumTable) + { + assert(pRule); + int n = pRule->IsContinusNum() ? WW8ListManager::nMinLevel : WW8ListManager::nMaxLevel; + for (int nLvl = 0; nLvl < n; ++nLvl) + { + const SwNumFormat& rFormat = pRule->Get(nLvl); + + if (rFormat.GetNumberingType() == SVX_NUM_CHAR_SPECIAL + || rFormat.GetNumberingType() == SVX_NUM_BITMAP) + { + const auto [s, pFont] = rExport.GetNumberingLevelBulletStringAndFont(rFormat); + (void)s; + assert(pFont); + GetId(*pFont); + } + } + } } sal_uInt16 wwFontHelper::GetId(const SvxFontItem& rFont) diff --git a/sw/source/filter/ww8/wrtww8.cxx b/sw/source/filter/ww8/wrtww8.cxx index d06267a7f2a3..7c4bb66fa760 100644 --- a/sw/source/filter/ww8/wrtww8.cxx +++ b/sw/source/filter/ww8/wrtww8.cxx @@ -3463,7 +3463,7 @@ ErrCode MSWordExportBase::ExportDocument( bool bWriteAll ) // fix the SwPositions in m_aFrames after SetRedlineFlags UpdateFramePositions(m_aFrames); - m_aFontHelper.InitFontTable(m_rDoc); + m_aFontHelper.InitFontTable(*this); GatherChapterFields(); CollectOutlineBookmarks(m_rDoc); diff --git a/sw/source/filter/ww8/wrtww8.hxx b/sw/source/filter/ww8/wrtww8.hxx index c52665f8d16b..dc7f37e3d959 100644 --- a/sw/source/filter/ww8/wrtww8.hxx +++ b/sw/source/filter/ww8/wrtww8.hxx @@ -319,7 +319,7 @@ private: public: wwFontHelper() : m_bLoadAllFonts(false) {} /// rDoc used only to get the initial standard font(s) in use. - void InitFontTable(const SwDoc& rDoc); + void InitFontTable(MSWordExportBase& rExport); sal_uInt16 GetId(const SvxFontItem& rFont); sal_uInt16 GetId(const wwFont& rFont); void WriteFontTable( SvStream *pTableStream, WW8Fib& pFib ); @@ -603,6 +603,7 @@ public: /// Return the numeric id of the numbering rule sal_uInt16 GetNumberingId( const SwNumRule& rNumRule ); + void EnsureUsedNumberingTable(); /// Return the numeric id of the style. sal_uInt16 GetId( const SwTextFormatColl& rColl ) const; @@ -770,6 +771,9 @@ public: /// Write one numbering level void NumberingLevel(SwNumRule const& rRule, sal_uInt8 nLvl); + std::pair<OUString, std::unique_ptr<wwFont>> + GetNumberingLevelBulletStringAndFont(const SwNumFormat& rLevelFormat); + // Convert the bullet according to the font. void SubstituteBullet( OUString& rNumStr, rtl_TextEncoding& rChrSet, OUString& rFontName ) const;