sw/qa/extras/ooxmlexport/data/tdf125469_singleSpacing.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport21.cxx | 36 +++++++++++++ sw/source/writerfilter/dmapper/DomainMapper.cxx | 36 +++++++++++-- sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx | 13 ++++ sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx | 10 +++ 5 files changed, 91 insertions(+), 4 deletions(-)
New commits: commit e3dfb54117f5325d8f38f58ef892c2808d601e54 Author: Justin Luth <jl...@mail.com> AuthorDate: Wed Jul 17 14:48:57 2024 -0400 Commit: Justin Luth <jl...@mail.com> CommitDate: Fri Jul 19 02:47:26 2024 +0200 tdf#125469 writerfilter: use "auto" for not-provided lineRule This fixes the problem where a programmatically generated document didn't specify a w:lineRule, and thus the w:line size was treated as a FIXed spacing of 12pt instead of as single spacing. ------------------------------------------------------------ lineRule (Spacing Between Lines) Specifies how the spacing between lines is calculated as stored in the line attribute. If this attribute is omitted, then it shall be assumed to be of a value "auto" if a w:line attribute value is present. ------------------------------------------------------------ However, our implementation (wisely) defaulted to ::FIX instead of ::PROP (which keeps precision in w:line sizes since PROP is a integer percentage instead of a float and thus loses precision when switching between FIX and PROP). Note of course that if neither w:line nor w:lineRule is provided, then we go with the LO default of mode=0, height=0 (aka AUTO). This gets tricky because the lineRule can be inherited, so just because it is missing in a pPr doesn't necessarily mean we can just substitute "auto". It is also tricky because w:spacing deals with more than just line spacing. Not surprisingly, no existing unit tests matched this case. However, the unit tests were VERY helpful in pointing out limitations as I attempted to code the fix. make CppunitTest_sw_ooxmlexport21 \ CPPUNIT_TEST_NAME=testTdf125469_singleSpacing Change-Id: Id5e95f2983f4fb435810d5a1f3fd25ded5118ddc Reviewed-on: https://gerrit.libreoffice.org/c/core/+/170670 Tested-by: Jenkins Reviewed-by: Justin Luth <jl...@mail.com> diff --git a/sw/qa/extras/ooxmlexport/data/tdf125469_singleSpacing.docx b/sw/qa/extras/ooxmlexport/data/tdf125469_singleSpacing.docx new file mode 100644 index 000000000000..ff92e1b2d0c7 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf125469_singleSpacing.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport21.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport21.cxx index 9ef569e47683..91fbf2e7e110 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport21.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport21.cxx @@ -327,6 +327,42 @@ DECLARE_OOXMLEXPORT_TEST(testTdf158597, "tdf158597.docx") } } +DECLARE_OOXMLEXPORT_TEST(testTdf125469_singleSpacing, "tdf125469_singleSpacing.docx") +{ + // Given a document with 4 paragraphs of varying strange line spacing definitions, + // and a DocDefault of single line spacing (AUTO 240pt) (240pt is 0.423 cm) + + // Paragraph 1 - DocDefaults specifies size 240 without a lineRule - default is AUTO(aka PROP) + // Visually, this should clearly say "Single spacing" + auto aSpacing = getProperty<style::LineSpacing>(getParagraph(1), u"ParaLineSpacing"_ustr); + CPPUNIT_ASSERT_EQUAL(sal_Int16(style::LineSpacingMode::PROP), aSpacing.Mode); + CPPUNIT_ASSERT_EQUAL(sal_Int16(100), aSpacing.Height); + + // Paragraph 2 - paragraph style specifies atLeast 240, para overrides with only -240. + // The negative value (always) turns the (inherited) "atLeast" into an "exact". + // Visually, this is hardly readable (36pt font forced into 12pt space) + aSpacing = getProperty<style::LineSpacing>(getParagraph(2), u"ParaLineSpacing"_ustr); + // CPPUNIT_ASSERT_EQUAL(sal_Int16(style::LineSpacingMode::FIX), aSpacing.Mode); + // CPPUNIT_ASSERT_EQUAL(sal_Int16(423), aSpacing.Height); + + // Paragraph 3 - paragraph style specifies exact 240, para overrides with exact -240. + // The negative value turns the non-inherited "exact" into an "atLeast". + // Visually, this should clearly say "Negative exact" + aSpacing = getProperty<style::LineSpacing>(getParagraph(3), u"ParaLineSpacing"_ustr); + // CPPUNIT_ASSERT_EQUAL(sal_Int16(style::LineSpacingMode::MINIMUM), aSpacing.Mode); + // CPPUNIT_ASSERT_EQUAL(sal_Int16(423), aSpacing.Height); + + // Paragraph 4 - paragraph style specifies exact 240, para overrides with only -240. + // The negative value does nothing to the inherited "exact". + // Visually, this is hardly readable (36pt font forced into 12pt space) + aSpacing = getProperty<style::LineSpacing>(getParagraph(4), u"ParaLineSpacing"_ustr); + // CPPUNIT_ASSERT_EQUAL(sal_Int16(style::LineSpacingMode::FIX), aSpacing.Mode); + // CPPUNIT_ASSERT_EQUAL(sal_Int16(423), aSpacing.Height); + + // all of this ends up being squeezed onto a single page + // CPPUNIT_ASSERT_EQUAL(1, getPages()); +} + DECLARE_OOXMLEXPORT_TEST(testTdf43767_caseMapNumbering, "tdf43767_caseMapNumbering.odt") { // given a document with 2 numbered Lists [each entry restarts numbering for visual comparison] diff --git a/sw/source/writerfilter/dmapper/DomainMapper.cxx b/sw/source/writerfilter/dmapper/DomainMapper.cxx index 3bc6444ef15e..13ffe529d09d 100644 --- a/sw/source/writerfilter/dmapper/DomainMapper.cxx +++ b/sw/source/writerfilter/dmapper/DomainMapper.cxx @@ -508,6 +508,11 @@ void DomainMapper::lcl_attribute(Id nName, Value & val) case NS_ooxml::LN_CT_Spacing_line: //91434 case NS_ooxml::LN_CT_Spacing_lineRule: //91435 { + if (nName == NS_ooxml::LN_CT_Spacing_line) + m_pImpl->SetSpacingHadLine(true); + else + m_pImpl->SetSpacingHadLineRule(true); + m_pImpl->SetLineSpacing(nName, nIntValue); } break; @@ -2398,12 +2403,37 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) } } break; + case NS_ooxml::LN_CT_PPrBase_spacing: + { + m_pImpl->SetSpacingHadLine(false); + m_pImpl->SetSpacingHadLineRule(false); + + resolveSprmProps(*this, rSprm); + + // The implementation default is FIXED line spacing instead of the documented default AUTO. + // If w:line seen, but no w:lineRule was provided, send appropriate mode to finalize height. + if (m_pImpl->GetSpacingHadLine() && !m_pImpl->GetSpacingHadLineRule()) + { + sal_Int32 nLineRule = NS_ooxml::LN_Value_doc_ST_LineSpacingRule_auto; // default + // lineRule may have been provided via inherited paragraph style. + style::LineSpacing aInheritedSpacing; + m_pImpl->GetInheritedParaProperty(PROP_PARA_LINE_SPACING) >>= aInheritedSpacing; + if (aInheritedSpacing.Mode == style::LineSpacingMode::FIX) + nLineRule = NS_ooxml::LN_Value_doc_ST_LineSpacingRule_exact; + else if (aInheritedSpacing.Mode == style::LineSpacingMode::MINIMUM) + nLineRule = NS_ooxml::LN_Value_doc_ST_LineSpacingRule_atLeast; + + m_pImpl->SetLineSpacing(NS_ooxml::LN_CT_Spacing_lineRule, nLineRule); + } + + m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, u"spacing"_ustr, m_pImpl->m_aSubInteropGrabBag); + } + break; case NS_ooxml::LN_CT_PPr_sectPr: case NS_ooxml::LN_EG_RPrBase_rFonts: case NS_ooxml::LN_EG_RPrBase_eastAsianLayout: case NS_ooxml::LN_EG_RPrBase_u: case NS_ooxml::LN_EG_RPrBase_lang: - case NS_ooxml::LN_CT_PPrBase_spacing: case NS_ooxml::LN_CT_PPrBase_ind: case NS_ooxml::LN_CT_RPrDefault_rPr: case NS_ooxml::LN_CT_PPrDefault_pPr: @@ -2415,9 +2445,7 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) if (nSprmId == NS_ooxml::LN_CT_PPr_sectPr) m_pImpl->SetParaSectpr(true); resolveSprmProps(*this, rSprm); - if (nSprmId == NS_ooxml::LN_CT_PPrBase_spacing) - m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, u"spacing"_ustr, m_pImpl->m_aSubInteropGrabBag); - else if (nSprmId == NS_ooxml::LN_EG_RPrBase_rFonts) + if (nSprmId == NS_ooxml::LN_EG_RPrBase_rFonts) m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, u"rFonts"_ustr, m_pImpl->m_aSubInteropGrabBag); else if (nSprmId == NS_ooxml::LN_EG_RPrBase_lang) m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, u"lang"_ustr, m_pImpl->m_aSubInteropGrabBag); diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx index 79ae5e629263..df28343d3186 100644 --- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx +++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx @@ -1536,6 +1536,19 @@ uno::Any DomainMapper_Impl::GetPropertyFromParaStyleSheet(PropertyIds eId) return GetPropertyFromStyleSheet(eId, pEntry, /*bDocDefaults=*/true, /*bPara=*/true); } +uno::Any DomainMapper_Impl::GetInheritedParaProperty(PropertyIds eId) +{ + StyleSheetEntryPtr pEntry; + if ( m_bInStyleSheetImport ) + pEntry = pEntry = GetStyleSheetTable()->FindStyleSheetByISTD( + GetStyleSheetTable()->GetCurrentEntry()->m_sBaseStyleIdentifier); + else + pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(GetCurrentParaStyleName()); + + const bool bCheckDocDefaults = !IsDocDefaultsImport(); + return GetPropertyFromStyleSheet(eId, pEntry, bCheckDocDefaults, /*bPara=*/true); +} + uno::Any DomainMapper_Impl::GetPropertyFromCharStyleSheet(PropertyIds eId, const PropertyMapPtr& rContext) { if ( m_bInStyleSheetImport || eId == PROP_CHAR_STYLE_NAME || !isCharacterProperty(eId) ) diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx b/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx index 5bb82fc715ae..e63c64778b09 100644 --- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx +++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx @@ -666,6 +666,10 @@ private: ::std::set<::std::pair<PagePartType, PageType>> m_HeaderFooterSeen; + // LN_CT_PPrBase_spacing has specified both a line and a lineRule? + bool m_bSpacingHadLine = false; + bool m_bSpacingHadLineRule = false; + //annotation import rtl::Reference< SwXTextField > m_xAnnotationField; sal_Int32 m_nAnnotationId; @@ -896,6 +900,8 @@ public: css::uno::Any GetPropertyFromStyleSheet(PropertyIds eId, StyleSheetEntryPtr pEntry, const bool bDocDefaults, const bool bPara, bool* bIsDocDefault = nullptr); // current paragraph style - including inherited properties css::uno::Any GetPropertyFromParaStyleSheet(PropertyIds eId); + // only get inherited property + css::uno::Any GetInheritedParaProperty(PropertyIds eId); // context's character style - including inherited properties css::uno::Any GetPropertyFromCharStyleSheet(PropertyIds eId, const PropertyMapPtr& rContext); // get property first from the given context, or secondly via inheritance from styles/docDefaults @@ -1212,6 +1218,10 @@ public: void SetParaAutoBefore(bool const bParaAutoBefore) { m_StreamStateStack.top().bParaAutoBefore = bParaAutoBefore; } + bool GetSpacingHadLine() const { return m_bSpacingHadLine; } + void SetSpacingHadLine(bool bSet) { m_bSpacingHadLine = bSet; } + bool GetSpacingHadLineRule() const { return m_bSpacingHadLineRule; } + void SetSpacingHadLineRule(bool bSet) { m_bSpacingHadLineRule = bSet; } void SetLineSpacing(const Id nName, sal_Int32 nIntValue); /// Forget about the previous paragraph, as it's not inside the same