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 cf2afb85305153aff5d7faca10216c4d0610dad0 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue Jun 13 15:02:20 2023 +0200 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Wed Jun 14 10:18:19 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. (cherry picked from commit 85f0a5d7bc54dfba75e8d6dd9c905bc1ac31d927) Change-Id: I771c7bed27fa2596153aa77c472c91b819fa4cb1 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/153035 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> 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 ba2bdfed3c3f..00fbebcb3f4e 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx @@ -406,6 +406,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 b954d74d1b4e..795d25ef8f82 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -7693,8 +7693,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 10780b9c964c..3680eab25d08 100644 --- a/writerfilter/source/dmapper/DomainMapper.cxx +++ b/writerfilter/source/dmapper/DomainMapper.cxx @@ -1993,6 +1993,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, @@ -3448,9 +3455,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 cb68954c7929..43ba2188849c 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 ac3cf2aa6032..0aa49dec631b 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -8898,12 +8898,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 f4fd01ddccc5..93f835c0b4e7 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx @@ -1088,7 +1088,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 1e10d4966dbf..e6834ad674b3 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())