sw/qa/extras/ooxmlexport/data/para-style-char-position.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport18.cxx | 18 ++++++++++ sw/source/filter/ww8/docxattributeoutput.cxx | 8 ++++ writerfilter/source/dmapper/DomainMapper.cxx | 21 ++++++++++-- writerfilter/source/dmapper/DomainMapper.hxx | 6 ++- writerfilter/source/dmapper/DomainMapper_Impl.cxx | 4 +- writerfilter/source/dmapper/DomainMapper_Impl.hxx | 2 - writerfilter/source/dmapper/StyleSheetTable.cxx | 1 8 files changed, 53 insertions(+), 7 deletions(-)
New commits: commit ddf8903e9b7528671e752d24717056f2db039464 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue Jun 13 15:02:20 2023 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Wed Jun 14 08:43:45 2023 +0200 DOCX filter: improve handling of negative <w:position> in paragraph styles The bugdoc has a <w:position w:val="-1"> in its Normal paragraph style, which is almost not visible in Word, but we mapped this to default subscript text in Writer, leading to very visible bad font height in practice. The root of the problem is that <w:position> works with an absolute offset in half-points, while Writer works in percentages, so the import/export code can only do a correct mapping in case the font size is known. This initial mapping was added in commit e70df84352d3670508a4666c97df44f82c1ce934 (try somewhat harder to read w:position (bnc#773061), 2012-08-07), and later commit d71cf6390a89ea6a4fab724e3a7996f28ca33661 (tdf#99602 writerfilter: import subscript into character style, 2019-10-04) gave up on this for character styles. Fix the problem by working with paragraph styles similar to what the binary DOC filter already does, just assuming that the font height from the style won't be overwritten, or will be overwritten together with a matching <w:position>. Do this only for negative <w:position> for now, as that's good enough for our needs. Do the opposite of this at export time. It would be still possible in the future to add native handling for absolute escapements, and then this mapping would not be needed at all. Change-Id: I771c7bed27fa2596153aa77c472c91b819fa4cb1 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152962 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins (cherry picked from commit 85f0a5d7bc54dfba75e8d6dd9c905bc1ac31d927) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/153011 diff --git a/sw/qa/extras/ooxmlexport/data/para-style-char-position.docx b/sw/qa/extras/ooxmlexport/data/para-style-char-position.docx new file mode 100644 index 000000000000..946ca0bf9cc2 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/para-style-char-position.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx index 50a058d19c9d..fbb87915369e 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx @@ -615,6 +615,24 @@ CPPUNIT_TEST_FIXTURE(Test, testNumberPortionFormatFromODT) assertXPath(pXmlDoc, "//w:pPr/w:rPr/w:sz", "val", "48"); } +CPPUNIT_TEST_FIXTURE(Test, testParaStyleCharPosition) +{ + // Given a loaded document where the Normal paragraph style has <w:position w:val="-1">: + createSwDoc("para-style-char-position.docx"); + + // When saving it back to DOCX: + save("Office Open XML Text"); + + // Then make sure that is not turned into a normal subscript text: + xmlDocUniquePtr pXmlDoc = parseExport("word/styles.xml"); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 1 + // - Actual : 0 + // - XPath '/w:styles/w:style[@w:styleId='Normal']/w:rPr/w:position' number of nodes is incorrect + // i.e. we wrote <w:vertAlign w:val="subscript"> instead of <w:position>. + assertXPath(pXmlDoc, "/w:styles/w:style[@w:styleId='Normal']/w:rPr/w:position", "val", "-1"); +} + CPPUNIT_TEST_FIXTURE(Test, testTdf150966_regularInset) { // Given a docx document with a rectangular shape with height cy="900000" (EMU), tIns="180000" diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index a75682761d84..ba5d14bd8c90 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -7791,8 +7791,14 @@ void DocxAttributeOutput::CharEscapement( const SvxEscapementItem& rEscapement ) OString sIss; short nEsc = rEscapement.GetEsc(), nProp = rEscapement.GetProportionalHeight(); + bool bParaStyle = false; + if (m_rExport.m_bStyDef && m_rExport.m_pCurrentStyle) + { + bParaStyle = m_rExport.m_pCurrentStyle->Which() == RES_TXTFMTCOLL; + } + // Simplify styles to avoid impossible complexity. Import and export as defaults only - if ( m_rExport.m_bStyDef && nEsc ) + if ( m_rExport.m_bStyDef && nEsc && !(bParaStyle && nEsc < 0)) { nProp = DFLT_ESC_PROP; nEsc = (nEsc > 0) ? DFLT_ESC_AUTO_SUPER : DFLT_ESC_AUTO_SUB; diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx index aa131a625ce6..b1d3b71009c6 100644 --- a/writerfilter/source/dmapper/DomainMapper.cxx +++ b/writerfilter/source/dmapper/DomainMapper.cxx @@ -2061,6 +2061,13 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) { // For some undocumented reason, MS Word seems to ignore this in docDefaults + const StyleSheetEntryPtr pCurrStyle = GetStyleSheetTable()->GetCurrentEntry(); + if (pCurrStyle && pCurrStyle->m_nStyleTypeCode == STYLE_TYPE_PARA && nIntValue < 0) + { + m_pImpl->deferCharacterProperty(nSprmId, uno::Any(nIntValue)); + break; + } + // DON'T FIXME: Truly calculating this for Character Styles will be tricky, // because it depends on the final fontsize - regardless of // where it is set. So at the style level, @@ -3534,9 +3541,19 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) } } -void DomainMapper::processDeferredCharacterProperties( const std::map< sal_Int32, uno::Any >& deferredCharacterProperties ) +void DomainMapper::ProcessDeferredStyleCharacterProperties() { - assert( m_pImpl->GetTopContextType() == CONTEXT_CHARACTER ); + assert(m_pImpl->GetTopContextType() == CONTEXT_STYLESHEET); + m_pImpl->processDeferredCharacterProperties(false); +} + +void DomainMapper::processDeferredCharacterProperties( + const std::map<sal_Int32, uno::Any>& deferredCharacterProperties, bool bCharContext) +{ + if (bCharContext) + { + assert(m_pImpl->GetTopContextType() == CONTEXT_CHARACTER); + } PropertyMapPtr rContext = m_pImpl->GetTopContext(); for( const auto& rProp : deferredCharacterProperties ) { diff --git a/writerfilter/source/dmapper/DomainMapper.hxx b/writerfilter/source/dmapper/DomainMapper.hxx index b9771b8ad749..a452e2e6e9bc 100644 --- a/writerfilter/source/dmapper/DomainMapper.hxx +++ b/writerfilter/source/dmapper/DomainMapper.hxx @@ -123,7 +123,11 @@ public: /** @see DomainMapper_Impl::processDeferredCharacterProperties() */ - void processDeferredCharacterProperties(const std::map<sal_Int32, css::uno::Any>& rDeferredCharacterProperties); + void processDeferredCharacterProperties( + const std::map<sal_Int32, css::uno::Any>& rDeferredCharacterProperties, + bool bCharContext = true); + + void ProcessDeferredStyleCharacterProperties(); /// Enable storing of seen tokens in a named grab bag. void enableInteropGrabBag(const OUString& aName); diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx index 14cdc0a155ca..41ca8bf8d45e 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -9046,12 +9046,12 @@ void DomainMapper_Impl::deferCharacterProperty(sal_Int32 id, const css::uno::Any m_deferredCharacterProperties[ id ] = value; } -void DomainMapper_Impl::processDeferredCharacterProperties() +void DomainMapper_Impl::processDeferredCharacterProperties(bool bCharContext) { // Actually process in DomainMapper, so that it's the same source file like normal processing. if( !m_deferredCharacterProperties.empty()) { - m_rDMapper.processDeferredCharacterProperties( m_deferredCharacterProperties ); + m_rDMapper.processDeferredCharacterProperties(m_deferredCharacterProperties, bCharContext); m_deferredCharacterProperties.clear(); } } diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx index 1e02fd8d3949..4668ebc8ac4c 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx @@ -1074,7 +1074,7 @@ public: Processes properties deferred using deferCharacterProperty(). To be called whenever the top CONTEXT_CHARACTER is going to be used (e.g. by appendText()). */ - void processDeferredCharacterProperties(); + void processDeferredCharacterProperties(bool bCharContext = true); sal_Int32 getNumberingProperty(const sal_Int32 nListId, sal_Int32 nListLevel, const OUString& aProp); /// Get a property of the current numbering style's current level. diff --git a/writerfilter/source/dmapper/StyleSheetTable.cxx b/writerfilter/source/dmapper/StyleSheetTable.cxx index a78ac4fda2db..a7c3dc7381c7 100644 --- a/writerfilter/source/dmapper/StyleSheetTable.cxx +++ b/writerfilter/source/dmapper/StyleSheetTable.cxx @@ -808,6 +808,7 @@ void StyleSheetTable::lcl_entry(writerfilter::Reference<Properties>::Pointer_t r m_pImpl->m_pCurrentEntry = pNewEntry; m_pImpl->m_rDMapper.PushStyleSheetProperties( m_pImpl->m_pCurrentEntry->m_pProperties.get() ); ref->resolve(*this); + m_pImpl->m_rDMapper.ProcessDeferredStyleCharacterProperties(); //append it to the table m_pImpl->m_rDMapper.PopStyleSheetProperties(); if( !m_pImpl->m_rDMapper.IsOOXMLImport() || !m_pImpl->m_pCurrentEntry->m_sStyleName.isEmpty())