diff options
Diffstat (limited to 'writerfilter')
29 files changed, 420 insertions, 220 deletions
diff --git a/writerfilter/inc/dmapper/resourcemodel.hxx b/writerfilter/inc/dmapper/resourcemodel.hxx index 695c6c9420..9cd5af27e3 100644 --- a/writerfilter/inc/dmapper/resourcemodel.hxx +++ b/writerfilter/inc/dmapper/resourcemodel.hxx @@ -220,6 +220,7 @@ public: virtual void endParagraphGroup() = 0; virtual void markLastParagraphInSection(){}; + virtual void markLastParagraph() {} // When finishing this paragraph, do not add new paragraph /** Receives start mark for group with the same character properties. diff --git a/writerfilter/qa/cppunittests/dmapper/DomainMapper.cxx b/writerfilter/qa/cppunittests/dmapper/DomainMapper.cxx index 8452da0203..885443c5a2 100644 --- a/writerfilter/qa/cppunittests/dmapper/DomainMapper.cxx +++ b/writerfilter/qa/cppunittests/dmapper/DomainMapper.cxx @@ -155,6 +155,12 @@ CPPUNIT_TEST_FIXTURE(Test, testFdo78333) // just care that it doesn't crash/assert loadFromFile(u"fdo78333-1-minimized.docx"); } + +CPPUNIT_TEST_FIXTURE(Test, testTdf158360) +{ + // just test that doc with annotation in TOC doesn't crash/assert + loadFromFile(u"tdf158360.docx"); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/qa/cppunittests/dmapper/DomainMapperTableHandler.cxx b/writerfilter/qa/cppunittests/dmapper/DomainMapperTableHandler.cxx index 5d8d5efc50..b72b819139 100644 --- a/writerfilter/qa/cppunittests/dmapper/DomainMapperTableHandler.cxx +++ b/writerfilter/qa/cppunittests/dmapper/DomainMapperTableHandler.cxx @@ -183,6 +183,20 @@ CPPUNIT_TEST_FIXTURE(Test, testDOCXFloatingTableHeader) // 2233 pages and then there was a layout loop. CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), nLastPage); } + +CPPUNIT_TEST_FIXTURE(Test, testDOCXFloatingTableFootnoteRedline) +{ + // Given a document with a floating table in a footnote, with track changes recorded (but not + // visible): + // When importing that document: + loadFromFile(u"floattable-footnote-redline.docx"); + + // Then make sure the table is imported as inline, till Writer layout is ready to handle + // floating tables in footnotes: + uno::Reference<drawing::XDrawPageSupplier> xModel(mxComponent, uno::UNO_QUERY); + uno::Reference<drawing::XDrawPage> xDrawPage = xModel->getDrawPage(); + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), xDrawPage->getCount()); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/writerfilter/qa/cppunittests/dmapper/data/floattable-footnote-redline.docx b/writerfilter/qa/cppunittests/dmapper/data/floattable-footnote-redline.docx Binary files differnew file mode 100644 index 0000000000..10904ce43e --- /dev/null +++ b/writerfilter/qa/cppunittests/dmapper/data/floattable-footnote-redline.docx diff --git a/writerfilter/qa/cppunittests/dmapper/data/tdf158360.docx b/writerfilter/qa/cppunittests/dmapper/data/tdf158360.docx Binary files differnew file mode 100644 index 0000000000..a46ee67c00 --- /dev/null +++ b/writerfilter/qa/cppunittests/dmapper/data/tdf158360.docx diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx index 7e81cfd430..7e4eeda7d3 100644 --- a/writerfilter/source/dmapper/DomainMapper.cxx +++ b/writerfilter/source/dmapper/DomainMapper.cxx @@ -138,6 +138,8 @@ DomainMapper::DomainMapper( const uno::Reference< uno::XComponentContext >& xCon m_pImpl->SetDocumentSettingsProperty("FrameAutowidthWithMorePara", uno::Any(true)); m_pImpl->SetDocumentSettingsProperty("FootnoteInColumnToPageEnd", uno::Any(true)); m_pImpl->SetDocumentSettingsProperty("TabAtLeftIndentForParagraphsInList", uno::Any(true)); + m_pImpl->SetDocumentSettingsProperty(u"NoGapAfterNoteNumber"_ustr, + uno::Any(true)); // Enable only for new documents, since pasting from clipboard can influence existing doc m_pImpl->SetDocumentSettingsProperty("NoNumberingShowFollowBy", uno::Any(true)); @@ -703,6 +705,12 @@ void DomainMapper::lcl_attribute(Id nName, Value & val) case NS_ooxml::LN_CT_PageMar_gutter: m_pImpl->SetPageMarginTwip( PAGE_MAR_GUTTER, nIntValue ); break; + case NS_ooxml::LN_CT_PaperSource_first: + m_pImpl->SetPaperSource(PAPER_SOURCE_FIRST, nIntValue); + break; + case NS_ooxml::LN_CT_PaperSource_other: + m_pImpl->SetPaperSource(PAPER_SOURCE_OTHER, nIntValue); + break; case NS_ooxml::LN_CT_Language_val: //90314 case NS_ooxml::LN_CT_Language_eastAsia: //90315 case NS_ooxml::LN_CT_Language_bidi: //90316 @@ -2419,8 +2427,6 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) } break; case NS_ooxml::LN_CT_PPrBase_framePr: - // Avoid frames if we're inside a structured document tag, would just cause outer tables fail to create. - if (!m_pImpl->GetSdt()) { PropertyMapPtr pContext = m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH); if( pContext ) @@ -2495,7 +2501,16 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) pSectionContext->SetGutterMargin(rPageMar.gutter); } break; - + case NS_ooxml::LN_EG_SectPrContents_paperSrc: + m_pImpl->InitPaperSource(); + resolveSprmProps(*this, rSprm); + OSL_ENSURE(pSectionContext, "SectionContext unavailable!"); + if(pSectionContext) + { + const PaperSource& rPaperSource = m_pImpl->GetPaperSource(); + pSectionContext->SetPaperSource( rPaperSource.first, rPaperSource.other ); + } + break; case NS_ooxml::LN_EG_SectPrContents_cols: { writerfilter::Reference<Properties>::Pointer_t pProperties = rSprm.getProps(); @@ -3258,7 +3273,8 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) * section is a table. So in case first element is a table add a dummy para * and remove it again when lcl_endSectionGroup is called */ - if(m_pImpl->m_nTableDepth == 0 && m_pImpl->GetIsFirstParagraphInSection() + if (m_pImpl->m_StreamStateStack.top().nTableDepth == 0 + && m_pImpl->GetIsFirstParagraphInSection() && !m_pImpl->GetIsDummyParaAddedForTableInSection() && !m_pImpl->GetIsTextFrameInserted() && !m_pImpl->GetIsPreviousParagraphFramed() && !IsInHeaderFooter()) { @@ -3266,7 +3282,7 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) } // if first paragraph style in table has break-before-page, transfer that setting to the table itself. - if( m_pImpl->m_nTableDepth == 0 ) + if (m_pImpl->m_StreamStateStack.top().nTableDepth == 0) { const uno::Any aBreakType(style::BreakType_PAGE_BEFORE); const PropertyMapPtr pParagraphProps = m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH); @@ -3287,11 +3303,11 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) } } - m_pImpl->m_nTableDepth++; + m_pImpl->m_StreamStateStack.top().nTableDepth++; } break; case NS_ooxml::LN_tblEnd: - m_pImpl->m_nTableDepth--; + m_pImpl->m_StreamStateStack.top().nTableDepth--; break; case NS_ooxml::LN_tcStart: m_pImpl->m_nTableCellDepth++; @@ -4417,8 +4433,6 @@ void DomainMapper::lcl_utext(const sal_Unicode *const data_, size_t len) xContext->Erase(PROP_NUMBERING_LEVEL); } finishParagraph(bRemove, bNoNumbering); - if (bRemove) - m_pImpl->RemoveLastParagraph(); m_pImpl->SetParaSectpr(false); } @@ -4483,14 +4497,24 @@ void DomainMapper::lcl_utext(const sal_Unicode *const data_, size_t len) } else if (m_pImpl->IsOpenFieldCommand() && !m_pImpl->IsForceGenericFields()) { - if (bInSdtBlockText && m_pImpl->m_pSdtHelper->hasUnusedText()) - m_pImpl->m_pSdtHelper->createPlainTextControl(); + if (bInSdtBlockText) + { + if (m_pImpl->m_pSdtHelper->hasUnusedText()) + m_pImpl->m_pSdtHelper->createPlainTextControl(); + else if (!m_pImpl->m_pSdtHelper->isFieldStartRangeSet()) + m_pImpl->m_pSdtHelper->setFieldStartRange(GetCurrentTextRange()->getEnd()); + } m_pImpl->AppendFieldCommand(sText); } else if( m_pImpl->IsOpenField() && m_pImpl->IsFieldResultAsString()) { - if (bInSdtBlockText && m_pImpl->m_pSdtHelper->hasUnusedText()) - m_pImpl->m_pSdtHelper->createPlainTextControl(); + if (bInSdtBlockText) + { + if (m_pImpl->m_pSdtHelper->hasUnusedText()) + m_pImpl->m_pSdtHelper->createPlainTextControl(); + else if (!m_pImpl->m_pSdtHelper->isFieldStartRangeSet()) + m_pImpl->m_pSdtHelper->setFieldStartRange(GetCurrentTextRange()->getEnd()); + } /*depending on the success of the field insert operation this result will be set at the field or directly inserted into the text*/ m_pImpl->AppendFieldResult(sText); @@ -4947,6 +4971,9 @@ void DomainMapper::finishParagraph(const bool bRemove, const bool bNoNumbering) if (m_pImpl->m_pSdtHelper->getControlType() == SdtControlType::datePicker) m_pImpl->m_pSdtHelper->createDateContentControl(); m_pImpl->finishParagraph(m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH), bRemove, bNoNumbering); + if (bRemove || mbIsLastPara) + m_pImpl->RemoveLastParagraph(); + mbIsLastPara = false; // handle other subdocuments } void DomainMapper::commentProps(const OUString& sId, const CommentProperties& rProps) diff --git a/writerfilter/source/dmapper/DomainMapper.hxx b/writerfilter/source/dmapper/DomainMapper.hxx index f9c163ab1f..e19dcd44bb 100644 --- a/writerfilter/source/dmapper/DomainMapper.hxx +++ b/writerfilter/source/dmapper/DomainMapper.hxx @@ -83,6 +83,7 @@ public: // Stream virtual void markLastParagraphInSection() override; + virtual void markLastParagraph() override { mbIsLastPara = true; } virtual void markLastSectionGroup() override; // BinaryObj @@ -188,6 +189,7 @@ private: bool mbIsSplitPara; bool mbHasControls; bool mbWasShapeInPara; + bool mbIsLastPara = false; std::unique_ptr< GraphicZOrderHelper > m_zOrderHelper; OUString m_sGlossaryEntryName; }; diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx index 2092ba4096..e63d1cf961 100644 --- a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx +++ b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx @@ -1599,7 +1599,12 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel) // m_xText points to the body text, get the current xText from m_rDMapper_Impl, in case e.g. we would be in a header. uno::Reference<text::XTextAppendAndConvert> xTextAppendAndConvert(m_rDMapper_Impl.GetTopTextAppend(), uno::UNO_QUERY); uno::Reference<beans::XPropertySet> xFrameAnchor; - if (xTextAppendAndConvert.is()) + + // Writer layout has problems with redlines on floating table rows in footnotes, avoid + // them. + bool bInFootnote = m_rDMapper_Impl.IsInFootOrEndnote(); + bool bRecordChanges = m_rDMapper_Impl.GetSettingsTable()->GetRecordChanges(); + if (xTextAppendAndConvert.is() && !(bInFootnote && bRecordChanges)) { std::deque<css::uno::Any> aFramedRedlines = m_rDMapper_Impl.m_aStoredRedlines[StoredRedlines::FRAME]; std::vector<sal_Int32> redPos, redLen; diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx index db2a712703..58568a9105 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -341,9 +341,6 @@ DomainMapper_Impl::DomainMapper_Impl( m_bSetCitation( false ), m_bSetDateValue( false ), m_bIsFirstSection( true ), - m_bIsColumnBreakDeferred( false ), - m_bIsPageBreakDeferred( false ), - m_nLineBreaksDeferred( 0 ), m_bSdtEndDeferred(false), m_bParaSdtEndDeferred(false), m_bStartTOC(false), @@ -352,7 +349,6 @@ DomainMapper_Impl::DomainMapper_Impl( m_bStartIndex(false), m_bStartBibliography(false), m_nStartGenericField(0), - m_bTextInserted(false), m_bTextDeleted(false), m_nLastRedlineMovedID(1), m_sCurrentPermId(0), @@ -361,10 +357,7 @@ DomainMapper_Impl::DomainMapper_Impl( m_bInStyleSheetImport( false ), m_bInNumberingImport(false), m_bInAnyTableImport( false ), - m_eInHeaderFooterImport( HeaderFooterImportState::none ), m_bDiscardHeaderFooter( false ), - m_bInFootOrEndnote(false), - m_bInFootnote(false), m_bHasFootnoteStyle(false), m_bCheckFootnoteStyle(false), m_eSkipFootnoteState(SkipFootnoteSeparator::OFF), @@ -377,16 +370,9 @@ DomainMapper_Impl::DomainMapper_Impl( m_bIsParaMarkerChange( false ), m_bIsParaMarkerMove( false ), m_bRedlineImageInPreviousRun( false ), - m_bParaChanged( false ), - m_bIsFirstParaInSection( true ), - m_bIsFirstParaInSectionAfterRedline( true ), m_bDummyParaAddedForTableInSection( false ), m_bTextFrameInserted(false), - m_bIsPreviousParagraphFramed( false ), - m_bIsLastParaInSection( false ), m_bIsLastSectionGroup( false ), - m_bIsInComments( false ), - m_bParaSectpr( false ), m_bUsingEnhancedFields( false ), m_bSdt(false), m_bIsFirstRun(false), @@ -399,7 +385,6 @@ DomainMapper_Impl::DomainMapper_Impl( m_bIsNewDoc(!rMediaDesc.getUnpackedValueOrDefault("InsertMode", false)), m_bIsAltChunk(rMediaDesc.getUnpackedValueOrDefault("AltChunkMode", false)), m_bIsReadGlossaries(rMediaDesc.getUnpackedValueOrDefault("ReadGlossaries", false)), - m_nTableDepth(0), m_nTableCellDepth(0), m_bHasFtn(false), m_bHasFtnSep(false), @@ -407,14 +392,10 @@ DomainMapper_Impl::DomainMapper_Impl( m_bIgnoreNextTab(false), m_bIsSplitPara(false), m_bIsActualParagraphFramed( false ), - m_bParaHadField(false), - m_bSaveParaHadField(false), m_bParaAutoBefore(false), - m_bFirstParagraphInCell(true), - m_bSaveFirstParagraphInCell(false), - m_bParaWithInlineObject(false), m_bSaxError(false) { + m_StreamStateStack.emplace(); // add state for document body m_aBaseUrl = rMediaDesc.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_DOCUMENTBASEURL, OUString()); if (m_aBaseUrl.isEmpty()) { @@ -454,6 +435,7 @@ DomainMapper_Impl::DomainMapper_Impl( DomainMapper_Impl::~DomainMapper_Impl() { + assert(!m_StreamStateStack.empty()); ChainTextFrames(); // Don't remove last paragraph when pasting, sw expects that empty paragraph. if (m_bIsNewDoc) @@ -947,27 +929,29 @@ void DomainMapper_Impl::SetIsLastSectionGroup( bool bIsLast ) void DomainMapper_Impl::SetIsLastParagraphInSection( bool bIsLast ) { - m_bIsLastParaInSection = bIsLast; + m_StreamStateStack.top().bIsLastParaInSection = bIsLast; } void DomainMapper_Impl::SetIsFirstParagraphInSection( bool bIsFirst ) { - m_bIsFirstParaInSection = bIsFirst; + m_StreamStateStack.top().bIsFirstParaInSection = bIsFirst; } void DomainMapper_Impl::SetIsFirstParagraphInSectionAfterRedline( bool bIsFirstAfterRedline ) { - m_bIsFirstParaInSectionAfterRedline = bIsFirstAfterRedline; + m_StreamStateStack.top().bIsFirstParaInSectionAfterRedline = bIsFirstAfterRedline; } bool DomainMapper_Impl::GetIsFirstParagraphInSection( bool bAfterRedline ) const { // Anchored objects may include multiple paragraphs, // and none of them should be considered the first para in section. - return ( bAfterRedline ? m_bIsFirstParaInSectionAfterRedline : m_bIsFirstParaInSection ) + return (bAfterRedline + ? m_StreamStateStack.top().bIsFirstParaInSectionAfterRedline + : m_StreamStateStack.top().bIsFirstParaInSection) && !IsInShape() - && !m_bIsInComments + && !IsInComments() && !IsInFootOrEndnote(); } @@ -987,13 +971,11 @@ void DomainMapper_Impl::SetIsTextFrameInserted( bool bIsInserted ) m_bTextFrameInserted = bIsInserted; } - void DomainMapper_Impl::SetParaSectpr(bool bParaSectpr) { - m_bParaSectpr = bParaSectpr; + m_StreamStateStack.top().bParaSectpr = bParaSectpr; } - void DomainMapper_Impl::SetSdt(bool bSdt) { m_bSdt = bSdt; @@ -1599,21 +1581,22 @@ ListsManager::Pointer const & DomainMapper_Impl::GetListTable() void DomainMapper_Impl::deferBreak( BreakType deferredBreakType) { + assert(!m_StreamStateStack.empty()); switch (deferredBreakType) { case LINE_BREAK: - m_nLineBreaksDeferred++; + m_StreamStateStack.top().nLineBreaksDeferred++; break; case COLUMN_BREAK: - m_bIsColumnBreakDeferred = true; + m_StreamStateStack.top().bIsColumnBreakDeferred = true; break; case PAGE_BREAK: // See SwWW8ImplReader::HandlePageBreakChar(), page break should be // ignored inside tables. - if (m_nTableDepth > 0) + if (0 < m_StreamStateStack.top().nTableDepth) return; - m_bIsPageBreakDeferred = true; + m_StreamStateStack.top().bIsPageBreakDeferred = true; break; default: return; @@ -1622,14 +1605,15 @@ void DomainMapper_Impl::deferBreak( BreakType deferredBreakType) bool DomainMapper_Impl::isBreakDeferred( BreakType deferredBreakType ) { + assert(!m_StreamStateStack.empty()); switch (deferredBreakType) { case LINE_BREAK: - return m_nLineBreaksDeferred > 0; + return 0 < m_StreamStateStack.top().nLineBreaksDeferred; case COLUMN_BREAK: - return m_bIsColumnBreakDeferred; + return m_StreamStateStack.top().bIsColumnBreakDeferred; case PAGE_BREAK: - return m_bIsPageBreakDeferred; + return m_StreamStateStack.top().bIsPageBreakDeferred; default: return false; } @@ -1637,17 +1621,18 @@ bool DomainMapper_Impl::isBreakDeferred( BreakType deferredBreakType ) void DomainMapper_Impl::clearDeferredBreak(BreakType deferredBreakType) { + assert(!m_StreamStateStack.empty()); switch (deferredBreakType) { case LINE_BREAK: - assert(m_nLineBreaksDeferred > 0); - m_nLineBreaksDeferred--; + assert(0 < m_StreamStateStack.top().nLineBreaksDeferred); + m_StreamStateStack.top().nLineBreaksDeferred--; break; case COLUMN_BREAK: - m_bIsColumnBreakDeferred = false; + m_StreamStateStack.top().bIsColumnBreakDeferred = false; break; case PAGE_BREAK: - m_bIsPageBreakDeferred = false; + m_StreamStateStack.top().bIsPageBreakDeferred = false; break; default: break; @@ -1656,9 +1641,10 @@ void DomainMapper_Impl::clearDeferredBreak(BreakType deferredBreakType) void DomainMapper_Impl::clearDeferredBreaks() { - m_nLineBreaksDeferred = 0; - m_bIsColumnBreakDeferred = false; - m_bIsPageBreakDeferred = false; + assert(!m_StreamStateStack.empty()); + m_StreamStateStack.top().nLineBreaksDeferred = 0; + m_StreamStateStack.top().bIsColumnBreakDeferred = false; + m_StreamStateStack.top().bIsPageBreakDeferred = false; } void DomainMapper_Impl::setSdtEndDeferred(bool bSdtEndDeferred) @@ -1731,7 +1717,11 @@ static void lcl_MoveBorderPropertiesToFrame(std::vector<beans::PropertyValue>& r aValue.Name = sPropertyName; aValue.Value = xTextRangeProperties->getPropertyValue(sPropertyName); if( nProperty < 4 ) + { xTextRangeProperties->setPropertyValue( sPropertyName, uno::Any(table::BorderLine2())); + if (!aValue.Value.hasValue()) + aValue.Value <<= table::BorderLine2(); + } else // border spacing { sal_Int32 nDistance = 0; @@ -2356,7 +2346,9 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con { if ( GetIsFirstParagraphInShape() || (GetIsFirstParagraphInSection() && GetSectionContext() && GetSectionContext()->IsFirstSection()) || - (m_bFirstParagraphInCell && m_nTableDepth > 0 && m_nTableDepth == m_nTableCellDepth) ) + (m_StreamStateStack.top().bFirstParagraphInCell + && 0 < m_StreamStateStack.top().nTableDepth + && m_StreamStateStack.top().nTableDepth == m_nTableCellDepth)) { // export requires grabbag to match top_margin, so keep them in sync if (nBeforeAutospacing && bIsAutoSet) @@ -2609,7 +2601,8 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con else { uno::Reference<text::XTextCursor> xCursor; - if (m_bParaHadField && !m_bIsInComments && !m_xTOCMarkerCursor.is()) + if (m_StreamStateStack.top().bParaHadField + && !IsInComments() && !m_xTOCMarkerCursor.is()) { // Workaround to make sure char props of the field are not lost. // Not relevant for editeng-based comments. @@ -2651,9 +2644,10 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con TOOLS_WARN_EXCEPTION("writerfilter", "DomainMapper_Impl::finishParagraph NumberingRules"); } } - else if ( m_xPreviousParagraph->getPropertySetInfo()->hasPropertyByName("NumberingStyleName") && + else if (m_xPreviousParagraph->getPropertySetInfo()->hasPropertyByName("NumberingStyleName") // don't update before tables - (m_nTableDepth == 0 || !m_bFirstParagraphInCell)) + && (m_StreamStateStack.top().nTableDepth == 0 + || !m_StreamStateStack.top().bFirstParagraphInCell)) { aCurrentNumberingName = GetListStyleName(nListId); m_xPreviousParagraph->getPropertyValue("NumberingStyleName") >>= aPreviousNumberingName; @@ -2808,7 +2802,7 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con // tdf#77417 trim right white spaces in table cells in 2010 compatibility mode sal_Int32 nMode = GetSettingsTable()->GetWordCompatibilityMode(); - if ( m_nTableDepth > 0 && nMode > 0 && nMode <= 14 ) + if (0 < m_StreamStateStack.top().nTableDepth && 0 < nMode && nMode <= 14) { // skip new line xCur->goLeft(1, false); @@ -2829,7 +2823,7 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con xCur->goLeft( 1 , true ); // Extend the redline ranges for empty paragraphs - if ( !m_bParaChanged && m_previousRedline ) + if (!m_StreamStateStack.top().bParaChanged && m_previousRedline) CreateRedline( xCur, m_previousRedline ); CheckParaMarkerRedline( xCur ); } @@ -2837,8 +2831,9 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con css::uno::Reference<css::beans::XPropertySet> xParaProps(xTextRange, uno::UNO_QUERY); // table style precedence and not hidden shapes anchored to hidden empty table paragraphs - if (xParaProps && !m_bIsInComments - && (m_nTableDepth > 0 || !m_aAnchoredObjectAnchors.empty())) + if (xParaProps && !IsInComments() + && (0 < m_StreamStateStack.top().nTableDepth + || !m_aAnchoredObjectAnchors.empty())) { // table style has got bigger precedence than docDefault style // collect these pending paragraph properties to process in endTable() @@ -2848,7 +2843,7 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con uno::Reference<text::XTextCursor> xCur2 = xTextRange->getText()->createTextCursorByRange(xCur); uno::Reference<text::XParagraphCursor> xParaCursor(xCur2, uno::UNO_QUERY_THROW); xParaCursor->gotoStartOfParagraph(false); - if (m_nTableDepth > 0) + if (0 < m_StreamStateStack.top().nTableDepth) { TableParagraph aPending{xParaCursor, xCur, pParaContext, xParaProps}; getTableManager().getCurrentParagraphs()->push_back(aPending); @@ -2972,23 +2967,23 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con else SetIsPreviousParagraphFramed(false); - m_bRemoveThisParagraph = false; + m_StreamStateStack.top().bRemoveThisParagraph = false; if( !IsInHeaderFooter() && !IsInShape() && (!pParaContext || !pParaContext->props().IsFrameMode()) ) { // If the paragraph is in a frame, shape or header/footer, it's not a paragraph of the section itself. SetIsFirstParagraphInSection(false); // don't count an empty deleted paragraph as first paragraph in section to avoid of // the deletion of the next empty paragraph later, resulting loss of the associated page break - if (!m_previousRedline || m_bParaChanged) + if (!m_previousRedline || m_StreamStateStack.top().bParaChanged) { SetIsFirstParagraphInSectionAfterRedline(false); SetIsLastParagraphInSection(false); } } m_previousRedline.clear(); - m_bParaChanged = false; + m_StreamStateStack.top().bParaChanged = false; - if (m_bIsInComments && pParaContext) + if (IsInComments() && pParaContext) { if (const OUString sParaId = pParaContext->props().GetParaId(); !sParaId.isEmpty()) { @@ -3011,15 +3006,19 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con } SetIsOutsideAParagraph(true); - m_bParaHadField = false; + m_StreamStateStack.top().bParaHadField = false; // don't overwrite m_bFirstParagraphInCell in table separator nodes // and in text boxes anchored to the first paragraph of table cells - if (m_nTableDepth > 0 && m_nTableDepth == m_nTableCellDepth && !IsInShape() && !m_bIsInComments) - m_bFirstParagraphInCell = false; + if (0 < m_StreamStateStack.top().nTableDepth + && m_StreamStateStack.top().nTableDepth == m_nTableCellDepth + && !IsInShape() && !IsInComments()) + { + m_StreamStateStack.top().bFirstParagraphInCell = false; + } m_bParaAutoBefore = false; - m_bParaWithInlineObject = false; + m_StreamStateStack.top().bParaWithInlineObject = false; #ifdef DBG_UTIL TagLogger::getInstance().endElement(); @@ -3187,7 +3186,7 @@ void DomainMapper_Impl::applyToggleAttributes(const PropertyMapPtr& pPropertyMap { applyToggleAttributes(pPropertyMap); // If we are in comments, then disable CharGrabBag, comment text doesn't support that. - uno::Sequence< beans::PropertyValue > aValues = pPropertyMap->GetPropertyValues(/*bCharGrabBag=*/!m_bIsInComments); + uno::Sequence<beans::PropertyValue> aValues = pPropertyMap->GetPropertyValues(/*bCharGrabBag=*/!IsInComments()); if (IsInTOC() || m_bStartIndex || m_bStartBibliography) for( auto& rValue : asNonConstRange(aValues) ) @@ -3254,11 +3253,14 @@ void DomainMapper_Impl::applyToggleAttributes(const PropertyMapPtr& pPropertyMap { xTOCTextCursor->goLeft(1, false); } - xTextRange = xTextAppend->insertTextPortion(rString, aValues, xTOCTextCursor); + if (IsInComments()) + xTextRange = xTextAppend->finishParagraphInsert(aValues, xTOCTextCursor); + else + xTextRange = xTextAppend->insertTextPortion(rString, aValues, xTOCTextCursor); SAL_WARN_IF(!xTextRange.is(), "writerfilter.dmapper", "insertTextPortion failed"); if (!xTextRange.is()) throw uno::Exception("insertTextPortion failed", nullptr); - m_bTextInserted = true; + m_StreamStateStack.top().bTextInserted = true; xTOCTextCursor->gotoRange(xTextRange->getEnd(), true); if (m_nStartGenericField == 0) { @@ -3299,7 +3301,7 @@ void DomainMapper_Impl::applyToggleAttributes(const PropertyMapPtr& pPropertyMap m_pParaMarkerRedlineMove.clear(); } CheckRedline( xTextRange ); - m_bParaChanged = true; + m_StreamStateStack.top().bParaChanged = true; //getTableManager( ).handle(xTextRange); } @@ -3779,11 +3781,6 @@ bool isContentEmpty(uno::Reference<text::XText> const& xText, uno::Reference<tex void DomainMapper_Impl::PushPageHeaderFooter(PagePartType ePagePartType, PageType eType) { - m_bSaveParaHadField = m_bParaHadField; - m_aHeaderFooterStack.push(HeaderFooterContext(m_bTextInserted, m_nTableDepth)); - m_bTextInserted = false; - m_nTableDepth = 0; - bool bHeader = ePagePartType == PagePartType::Header; const PropertyIds ePropIsOn = bHeader ? PROP_HEADER_IS_ON: PROP_FOOTER_IS_ON; @@ -3793,7 +3790,7 @@ void DomainMapper_Impl::PushPageHeaderFooter(PagePartType ePagePartType, PageTyp const PropertyIds ePropTextRight = bHeader ? PROP_HEADER_TEXT: PROP_FOOTER_TEXT; m_bDiscardHeaderFooter = true; - m_eInHeaderFooterImport = bHeader ? HeaderFooterImportState::header : HeaderFooterImportState::footer; + m_StreamStateStack.top().eSubstreamType = bHeader ? SubstreamType::Header : SubstreamType::Footer; //get the section context SectionPropertyMap* pSectionContext = GetSectionContext();; @@ -3822,7 +3819,10 @@ void DomainMapper_Impl::PushPageHeaderFooter(PagePartType ePagePartType, PageTyp if (eType == PageType::LEFT) { if (bHeader) + { pSectionContext->m_bLeftHeader = true; + pSectionContext->m_bHadLeftHeader = true; + } else pSectionContext->m_bLeftFooter = true; @@ -3831,7 +3831,10 @@ void DomainMapper_Impl::PushPageHeaderFooter(PagePartType ePagePartType, PageTyp else if (eType == PageType::FIRST) { if (bHeader) + { pSectionContext->m_bFirstHeader = true; + pSectionContext->m_bHadFirstHeader = true; + } else pSectionContext->m_bFirstFooter = true; @@ -3840,7 +3843,10 @@ void DomainMapper_Impl::PushPageHeaderFooter(PagePartType ePagePartType, PageTyp else { if (bHeader) + { pSectionContext->m_bRightHeader = true; + pSectionContext->m_bHadRightHeader = true; + } else pSectionContext->m_bRightFooter = true; @@ -3942,25 +3948,13 @@ void DomainMapper_Impl::PopPageHeaderFooter(PagePartType ePagePartType, PageType } m_bDiscardHeaderFooter = false; } - m_eInHeaderFooterImport = HeaderFooterImportState::none; - - if (!m_aHeaderFooterStack.empty()) - { - m_bTextInserted = m_aHeaderFooterStack.top().getTextInserted(); - m_nTableDepth = m_aHeaderFooterStack.top().getTableDepth(); - m_aHeaderFooterStack.pop(); - } - - m_bParaHadField = m_bSaveParaHadField; } void DomainMapper_Impl::PushFootOrEndnote( bool bIsFootnote ) { - SAL_WARN_IF(m_bInFootOrEndnote, "writerfilter.dmapper", "PushFootOrEndnote() is called from another foot or endnote"); - m_bInFootOrEndnote = true; - m_bInFootnote = bIsFootnote; + SAL_WARN_IF(m_StreamStateStack.top().eSubstreamType != SubstreamType::Body, "writerfilter.dmapper", "PushFootOrEndnote() is called from another foot or endnote"); + m_StreamStateStack.top().eSubstreamType = bIsFootnote ? SubstreamType::Footnote : SubstreamType::Endnote; m_bCheckFirstFootnoteTab = true; - m_bSaveFirstParagraphInCell = m_bFirstParagraphInCell; try { // Redlines outside the footnote should not affect footnote content @@ -4108,7 +4102,7 @@ void DomainMapper_Impl::CreateRedline(uno::Reference<text::XTextRange> const& xR } // store frame and (possible floating) table redline data for restoring them after frame conversion enum StoredRedlines eType; - if (m_bIsActualParagraphFramed || m_nTableDepth > 0) + if (m_bIsActualParagraphFramed || 0 < m_StreamStateStack.top().nTableDepth) eType = StoredRedlines::FRAME; else if (IsInFootOrEndnote()) eType = IsInFootnote() ? StoredRedlines::FOOTNOTE : StoredRedlines::ENDNOTE; @@ -4166,7 +4160,8 @@ void DomainMapper_Impl::CheckRedline( uno::Reference< text::XTextRange > const& // only export ParagraphFormat, when there is no other redline in the same text portion to avoid missing redline compression, // but always export the first ParagraphFormat redline in a paragraph to keep the paragraph style change data for rejection - if( (!bUsedRange || !m_bParaChanged) && GetTopContextOfType(CONTEXT_PARAGRAPH) ) + if ((!bUsedRange || !m_StreamStateStack.top().bParaChanged) + && GetTopContextOfType(CONTEXT_PARAGRAPH)) { std::vector<RedlineParamsPtr>& avRedLines = GetTopContextOfType(CONTEXT_PARAGRAPH)->Redlines(); for( const auto& rRedline : avRedLines ) @@ -4225,7 +4220,7 @@ void DomainMapper_Impl::PushAnnotation() { try { - m_bIsInComments = true; + m_StreamStateStack.top().eSubstreamType = SubstreamType::Annotation; if (!GetTextFactory().is()) return; m_xAnnotationField.set( GetTextFactory()->createInstance( "com.sun.star.text.TextField.Annotation" ), @@ -4487,16 +4482,13 @@ void DomainMapper_Impl::PopFootOrEndnote() } m_aRedlines.pop(); m_eSkipFootnoteState = SkipFootnoteSeparator::OFF; - m_bInFootOrEndnote = m_bInFootnote = false; m_pFootnoteContext = nullptr; - m_bFirstParagraphInCell = m_bSaveFirstParagraphInCell; } void DomainMapper_Impl::PopAnnotation() { RemoveLastParagraph(); - m_bIsInComments = false; m_aTextAppendStack.pop(); try @@ -4814,7 +4806,7 @@ void DomainMapper_Impl::PushShapeContext( const uno::Reference< drawing::XShape getPropertyName( PROP_OPAQUE ), uno::Any( true ) ); } - m_bParaChanged = true; + m_StreamStateStack.top().bParaChanged = true; getTableManager().setIsInShape(true); } catch ( const uno::Exception& ) @@ -4964,7 +4956,7 @@ bool DomainMapper_Impl::IsDiscardHeaderFooter() const void DomainMapper_Impl::ClearPreviousParagraph() { // in table cells, set bottom auto margin of last paragraph to 0, except in paragraphs with numbering - if ((m_nTableDepth == (m_nTableCellDepth + 1)) + if ((m_StreamStateStack.top().nTableDepth == (m_nTableCellDepth + 1)) && m_xPreviousParagraph.is() && hasTableManager() && getTableManager().isCellLastParaAfterAutospacing()) { @@ -4976,7 +4968,7 @@ void DomainMapper_Impl::ClearPreviousParagraph() m_xPreviousParagraph.clear(); // next table paragraph will be first paragraph in a cell - m_bFirstParagraphInCell = true; + m_StreamStateStack.top().bFirstParagraphInCell = true; } void DomainMapper_Impl::HandleAltChunk(const OUString& rStreamName) @@ -5926,7 +5918,7 @@ uno::Reference<beans::XPropertySet> DomainMapper_Impl::FindOrCreateFieldMaster(c void DomainMapper_Impl::PushFieldContext() { - m_bParaHadField = true; + m_StreamStateStack.top().bParaHadField = true; if(m_bDiscardHeaderFooter) return; #ifdef DBG_UTIL @@ -5971,18 +5963,6 @@ void DomainMapper_Impl::SetFieldLocked() m_aFieldStack.back()->SetFieldLocked(); } -HeaderFooterContext::HeaderFooterContext(bool bTextInserted, sal_Int32 nTableDepth) - : m_bTextInserted(bTextInserted) - , m_nTableDepth(nTableDepth) -{ -} - -bool HeaderFooterContext::getTextInserted() const -{ - return m_bTextInserted; -} - -sal_Int32 HeaderFooterContext::getTableDepth() const { return m_nTableDepth; } FieldContext::FieldContext(uno::Reference< text::XTextRange > xStart) : m_bFieldCommandCompleted(false) @@ -6809,15 +6789,15 @@ OUString DomainMapper_Impl::extractTocTitle() css::uno::Reference<css::beans::XPropertySet> DomainMapper_Impl::StartIndexSectionChecked(const OUString& sServiceName) { - if (m_bParaChanged) + if (m_StreamStateStack.top().bParaChanged) { - finishParagraph(GetTopContextOfType(CONTEXT_PARAGRAPH), false); // resets m_bParaChanged + finishParagraph(GetTopContextOfType(CONTEXT_PARAGRAPH), false); // resets bParaChanged PopProperties(CONTEXT_PARAGRAPH); PushProperties(CONTEXT_PARAGRAPH); SetIsFirstRun(true); // The first paragraph of the index that is continuation of just finished one needs to be - // removed when finished (unless more content will arrive, which will set m_bParaChanged) - m_bRemoveThisParagraph = true; + // removed when finished (unless more content will arrive, which will set bParaChanged) + m_StreamStateStack.top().bRemoveThisParagraph = true; } const auto& xTextAppend = GetTopTextAppend(); const auto xTextRange = xTextAppend->getEnd(); @@ -7051,7 +7031,7 @@ void DomainMapper_Impl::handleToc m_bStartTOC = true; pContext->SetTOC(xTOC); - m_bParaHadField = false; + m_StreamStateStack.top().bParaHadField = false; if (!xTOC) return; @@ -7284,7 +7264,7 @@ void DomainMapper_Impl::handleBibliography xTOC->setPropertyValue(getPropertyName( PROP_TITLE ), uno::Any(OUString())); pContext->SetTOC( xTOC ); - m_bParaHadField = false; + m_StreamStateStack.top().bParaHadField = false; uno::Reference< text::XTextContent > xToInsert( xTOC, uno::UNO_QUERY ); appendTextContent(xToInsert, uno::Sequence< beans::PropertyValue >() ); @@ -7327,7 +7307,7 @@ void DomainMapper_Impl::handleIndex } } pContext->SetTOC( xTOC ); - m_bParaHadField = false; + m_StreamStateStack.top().bParaHadField = false; uno::Reference< text::XTextContent > xToInsert( xTOC, uno::UNO_QUERY ); appendTextContent(xToInsert, uno::Sequence< beans::PropertyValue >() ); @@ -8345,7 +8325,7 @@ void DomainMapper_Impl::CloseFieldCommand() } } else - m_bParaHadField = false; + m_StreamStateStack.top().bParaHadField = false; } } catch( const uno::Exception& ) @@ -8642,7 +8622,7 @@ void DomainMapper_Impl::PopFieldContext() if (m_bStartedTOC || m_bStartIndex || m_bStartBibliography) { // inside SDT, last empty paragraph is also part of index - if (!m_bParaChanged && !m_xSdtEntryStart) + if (!m_StreamStateStack.top().bParaChanged && !m_xSdtEntryStart) { // End of index is the first item on a new paragraph - this paragraph // should not be part of index @@ -8667,8 +8647,8 @@ void DomainMapper_Impl::PopFieldContext() } m_bStartedTOC = false; m_aTextAppendStack.pop(); - m_bTextInserted = false; - m_bParaChanged = true; // the paragraph must stay anyway + m_StreamStateStack.top().bTextInserted = false; + m_StreamStateStack.top().bParaChanged = true; // the paragraph must stay anyway } m_bStartTOC = false; m_bStartIndex = false; @@ -8789,9 +8769,9 @@ void DomainMapper_Impl::PopFieldContext() { --m_nStartGenericField; PopFieldmark(m_aTextAppendStack, xCrsr, pContext->GetFieldId()); - if(m_bTextInserted) + if (m_StreamStateStack.top().bTextInserted) { - m_bTextInserted = false; + m_StreamStateStack.top().bTextInserted = false; } } } @@ -8871,8 +8851,10 @@ void DomainMapper_Impl::StartOrEndBookmark( const OUString& rId ) * iff the first element in the section is a table. If the dummy para is not added yet, then add it; * So bookmark is not attached to the wrong paragraph. */ - if(hasTableManager() && getTableManager().isInCell() && m_nTableDepth == 0 && GetIsFirstParagraphInSection() - && !GetIsDummyParaAddedForTableInSection() &&!GetIsTextFrameInserted()) + if (hasTableManager() && getTableManager().isInCell() + && m_StreamStateStack.top().nTableDepth == 0 + && GetIsFirstParagraphInSection() + && !GetIsDummyParaAddedForTableInSection() && !GetIsTextFrameInserted()) { AddDummyParaForTableInSection(); } @@ -8913,8 +8895,11 @@ void DomainMapper_Impl::StartOrEndBookmark( const OUString& rId ) // keep bookmark range, if it doesn't exceed cell boundary uno::Reference< text::XTextRange > xStart = xCursor->getStart(); xCursor->goLeft( 1, false ); - if (m_nTableDepth == 0 || !m_bFirstParagraphInCell) + if (m_StreamStateStack.top().nTableDepth == 0 + || !m_StreamStateStack.top().bFirstParagraphInCell) + { xCursor->gotoRange(xStart, true ); + } } uno::Reference< container::XNamed > xBkmNamed( xBookmark, uno::UNO_QUERY_THROW ); SAL_WARN_IF(aBookmarkIter->second.m_sBookmarkName.isEmpty(), "writerfilter.dmapper", "anonymous bookmark"); @@ -8992,7 +8977,8 @@ void DomainMapper_Impl::startOrEndPermissionRange(sal_Int32 permissinId) * if the first element in the section is a table. If the dummy para is not added yet, then add it; * So permission is not attached to the wrong paragraph. */ - if (getTableManager().isInCell() && m_nTableDepth == 0 && GetIsFirstParagraphInSection() + if (getTableManager().isInCell() + && m_StreamStateStack.top().nTableDepth == 0 && GetIsFirstParagraphInSection() && !GetIsDummyParaAddedForTableInSection() && !GetIsTextFrameInserted()) { AddDummyParaForTableInSection(); @@ -9259,7 +9245,7 @@ void DomainMapper_Impl::ImportGraphic(const writerfilter::Reference<Properties> } else if (m_eGraphicImportType == IMPORT_AS_DETECTED_INLINE) { - m_bParaWithInlineObject = true; + m_StreamStateStack.top().bParaWithInlineObject = true; // store inline images with track changes, because the anchor point // to set redlining is not available yet @@ -9331,6 +9317,14 @@ void DomainMapper_Impl::SetPageMarginTwip( PageMarElement eElement, sal_Int32 nV } } +void DomainMapper_Impl::SetPaperSource(PaperSourceElement eElement, sal_Int32 nValue) +{ + if(eElement == PAPER_SOURCE_FIRST) + m_aPaperSource.first = nValue; + else + m_aPaperSource.other = nValue; +} + PageMar::PageMar() : top(ConversionHelper::convertTwipToMM100( sal_Int32(1440))) @@ -9751,6 +9745,8 @@ void DomainMapper_Impl::substream(Id rName, appendTableHandler(); getTableManager().startLevel(); + m_StreamStateStack.emplace(); + //import of page header/footer //Ensure that only one header/footer per section is pushed @@ -9781,8 +9777,12 @@ void DomainMapper_Impl::substream(Id rName, case NS_ooxml::LN_annotation : PushAnnotation(); break; + default: + assert(false); // unexpected? } + assert(m_StreamStateStack.top().eSubstreamType != SubstreamType::Body); + try { ref->resolve(m_rDMapper); @@ -9822,6 +9822,9 @@ void DomainMapper_Impl::substream(Id rName, break; } + assert(!m_StreamStateStack.empty()); + m_StreamStateStack.pop(); + getTableManager().endLevel(); popTableManager(); m_bHasFtn = bHasFtn; diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx index fbca4aa1f3..082eda8fc5 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx @@ -103,6 +103,23 @@ enum PageMarElement PAGE_MAR_GUTTER }; +struct PaperSource +{ + sal_Int32 first; + sal_Int32 other; + public: + PaperSource() : + first(0), + other(0) + {} +}; + +enum PaperSourceElement +{ + PAPER_SOURCE_FIRST, + PAPER_SOURCE_OTHER, +}; + /// property stack element enum ContextType { @@ -146,22 +163,57 @@ enum StoredRedlines NONE }; +enum class SubstreamType +{ + Body, + Header, + Footer, + Footnote, + Endnote, + Annotation, +}; + /** * Storage for state that is relevant outside a header/footer, but not inside it. * * In case some state of DomainMapper_Impl should be reset before handling the * header/footer and should be restored once handling of header/footer is done, * then you can use this class to do so. + * + * note: presumably more state should be moved here. */ -class HeaderFooterContext +struct SubstreamContext { - bool m_bTextInserted; - sal_Int32 m_nTableDepth; - -public: - explicit HeaderFooterContext(bool bTextInserted, sal_Int32 nTableDepth); - bool getTextInserted() const; - sal_Int32 getTableDepth() const; + SubstreamType eSubstreamType = SubstreamType::Body; + bool bTextInserted = false; + /** + * This contains the raw table depth. nTableDepth > 0 is the same as + * getTableManager().isInTable(), unless we're in the first paragraph of a + * table, or first paragraph after a table, as the table manager is only + * updated once we ended the paragraph (and know if the para has the + * inTbl SPRM or not). + */ + sal_Int32 nTableDepth = 0; + // deferred breaks need to be saved for RTF, also for DOCX annotations + bool bIsColumnBreakDeferred = false; + bool bIsPageBreakDeferred = false; + sal_Int32 nLineBreaksDeferred = 0; + /// Current paragraph had at least one field in it. + bool bParaHadField = false; + /// Current paragraph in a table is first paragraph of a cell + bool bFirstParagraphInCell = true; + /// If the current paragraph has any runs. + bool bParaChanged = false; + bool bIsFirstParaInSectionAfterRedline = true; + bool bIsFirstParaInSection = true; + bool bIsLastParaInSection = false; + /// If the current paragraph contains section property definitions. + bool bParaSectpr = false; + bool bIsPreviousParagraphFramed = false; + /// Current paragraph had at least one inline object in it. + bool bParaWithInlineObject = false; + /// This is a continuation of already finished paragraph - e.g., first in an index section + bool bRemoveThisParagraph = false; }; /// Information about a paragraph to be finished after a field end. @@ -460,7 +512,9 @@ private: std::stack<TextAppendContext> m_aTextAppendStack; std::stack<AnchoredContext> m_aAnchoredStack; - std::stack<HeaderFooterContext> m_aHeaderFooterStack; +public: // DomainMapper needs it + std::stack<SubstreamContext> m_StreamStateStack; +private: std::stack<std::pair<TextAppendContext, PagePartType>> m_aHeaderFooterTextAppendStack; std::deque<FieldContextPtr> m_aFieldStack; @@ -471,9 +525,6 @@ private: bool m_bSetCitation; bool m_bSetDateValue; bool m_bIsFirstSection; - bool m_bIsColumnBreakDeferred; - bool m_bIsPageBreakDeferred; - sal_Int32 m_nLineBreaksDeferred; /// If we want to set "sdt end" on the next character context. bool m_bSdtEndDeferred; /// If we want to set "paragraph sdt end" on the next paragraph context. @@ -485,7 +536,6 @@ private: bool m_bStartIndex; bool m_bStartBibliography; unsigned int m_nStartGenericField; - bool m_bTextInserted; bool m_bTextDeleted; LineNumberSettings m_aLineNumberSettings; @@ -504,6 +554,7 @@ private: OUString m_sCurrentPermEdGrp; PageMar m_aPageMargins; + PaperSource m_aPaperSource; SymbolData m_aSymbolData; // TableManagers are stacked: one for each stream to avoid any confusion @@ -537,15 +588,7 @@ private: bool m_bInStyleSheetImport; //in import of fonts, styles, lists or lfos bool m_bInNumberingImport; //in import of numbering (i.e. numbering.xml) bool m_bInAnyTableImport; //in import of fonts, styles, lists or lfos - enum class HeaderFooterImportState - { - none, - header, - footer, - } m_eInHeaderFooterImport; bool m_bDiscardHeaderFooter; - bool m_bInFootOrEndnote; - bool m_bInFootnote; PropertyMapPtr m_pFootnoteContext; bool m_bHasFootnoteStyle; bool m_bCheckFootnoteStyle; @@ -583,26 +626,15 @@ private: // text ZWSPs to keep the change tracking of the image in Writer.) bool m_bRedlineImageInPreviousRun; - /// If the current paragraph has any runs. - bool m_bParaChanged; - bool m_bIsFirstParaInSection; - bool m_bIsFirstParaInSectionAfterRedline; bool m_bIsFirstParaInShape = false; bool m_bDummyParaAddedForTableInSection; bool m_bTextFrameInserted; - bool m_bIsPreviousParagraphFramed; - bool m_bIsLastParaInSection; bool m_bIsLastSectionGroup; - bool m_bIsInComments; - /// If the current paragraph contains section property definitions. - bool m_bParaSectpr; bool m_bUsingEnhancedFields; /// If the current paragraph is inside a structured document element. bool m_bSdt; bool m_bIsFirstRun; bool m_bIsOutsideAParagraph; - /// This is a continuation of already finished paragraph - e.g., first in an index section - bool m_bRemoveThisParagraph = false; css::uno::Reference< css::text::XTextCursor > m_xTOCMarkerCursor; @@ -701,7 +733,7 @@ public: void SetIsDecimalComma() { m_bIsDecimalComma = true; }; void SetIsLastParagraphInSection( bool bIsLast ); - bool GetIsLastParagraphInSection() const { return m_bIsLastParaInSection;} + bool GetIsLastParagraphInSection() const { return m_StreamStateStack.top().bIsLastParaInSection; } void SetRubySprmId( sal_uInt32 nSprmId) { m_aRubyInfo.nSprmId = nSprmId ; } void SetRubyText( OUString const &sText, OUString const &sStyle) { m_aRubyInfo.sRubyText = sText; @@ -725,10 +757,11 @@ public: bool GetIsTextFrameInserted() const { return m_bTextFrameInserted;} void SetIsTextDeleted(bool bIsTextDeleted) { m_bTextDeleted = bIsTextDeleted; } - void SetIsPreviousParagraphFramed( bool bIsFramed ) { m_bIsPreviousParagraphFramed = bIsFramed; } - bool GetIsPreviousParagraphFramed() const { return m_bIsPreviousParagraphFramed; } + void SetIsPreviousParagraphFramed(bool const bIsFramed) + { m_StreamStateStack.top().bIsPreviousParagraphFramed = bIsFramed; } + bool GetIsPreviousParagraphFramed() const { return m_StreamStateStack.top().bIsPreviousParagraphFramed; } void SetParaSectpr(bool bParaSectpr); - bool GetParaSectpr() const { return m_bParaSectpr;} + bool GetParaSectpr() const { return m_StreamStateStack.top().bParaSectpr; } void SetSymbolChar( sal_Int32 nSymbol) { m_aSymbolData.cSymbol = sal_Unicode(nSymbol); } void SetSymbolFont( OUString const &rName ) { m_aSymbolData.sFont = rName; } @@ -744,9 +777,9 @@ public: /// Getter method for m_bSdt. bool GetSdt() const { return m_bSdt;} - bool GetParaChanged() const { return m_bParaChanged;} - bool GetParaHadField() const { return m_bParaHadField; } - bool GetRemoveThisPara() const { return m_bRemoveThisParagraph; } + bool GetParaChanged() const { return m_StreamStateStack.top().bParaChanged; } + bool GetParaHadField() const { return m_StreamStateStack.top().bParaHadField; } + bool GetRemoveThisPara() const { return m_StreamStateStack.top().bRemoveThisParagraph; } void deferBreak( BreakType deferredBreakType ); bool isBreakDeferred( BreakType deferredBreakType ); @@ -865,7 +898,7 @@ public: css::uno::Reference<css::drawing::XShape> PopPendingShape(); void PopPageHeaderFooter(PagePartType ePagePartType, PageType eType); - bool IsInHeaderFooter() const { return m_eInHeaderFooterImport != HeaderFooterImportState::none; } + bool IsInHeaderFooter() const { auto const type(m_StreamStateStack.top().eSubstreamType); return type == SubstreamType::Header || type == SubstreamType::Footer; } void ConvertHeaderFooterToTextFrame(bool, bool); static void fillEmptyFrameProperties(std::vector<css::beans::PropertyValue>& rFrameProperties, bool bSetAnchorToChar); @@ -873,8 +906,8 @@ public: void PushFootOrEndnote( bool bIsFootnote ); void PopFootOrEndnote(); - bool IsInFootOrEndnote() const { return m_bInFootOrEndnote; } - bool IsInFootnote() const { return IsInFootOrEndnote() && m_bInFootnote; } + bool IsInFootOrEndnote() const { auto const type(m_StreamStateStack.top().eSubstreamType); return type == SubstreamType::Footnote || type == SubstreamType::Endnote; } + bool IsInFootnote() const { return m_StreamStateStack.top().eSubstreamType == SubstreamType::Footnote; } void StartCustomFootnote(const PropertyMapPtr pContext); void EndCustomFootnote(); @@ -1014,13 +1047,17 @@ public: void SetPageMarginTwip( PageMarElement eElement, sal_Int32 nValue ); const PageMar& GetPageMargins() const {return m_aPageMargins;} + void InitPaperSource() { m_aPaperSource = PaperSource(); } + void SetPaperSource( PaperSourceElement eElement, sal_Int32 nValue ); + const PaperSource& GetPaperSource() {return m_aPaperSource;} + const LineNumberSettings& GetLineNumberSettings() const { return m_aLineNumberSettings;} void SetLineNumberSettings(const LineNumberSettings& rSet) { m_aLineNumberSettings = rSet;} void SetInFootnoteProperties(bool bSet) { m_bIsInFootnoteProperties = bSet;} bool IsInFootnoteProperties() const { return m_bIsInFootnoteProperties;} - bool IsInComments() const { return m_bIsInComments; }; + bool IsInComments() const { return m_StreamStateStack.top().eSubstreamType == SubstreamType::Annotation; }; std::vector<css::beans::PropertyValue> MakeFrameProperties(const ParagraphProperties& rProps); void CheckUnregisteredFrameConversion(bool bPreventOverlap = false); @@ -1104,14 +1141,6 @@ public: /// Document background color, applied to every page style. std::optional<sal_Int32> m_oBackgroundColor; - /** - * This contains the raw table depth. m_nTableDepth > 0 is the same as - * getTableManager().isInTable(), unless we're in the first paragraph of a - * table, or first paragraph after a table, as the table manager is only - * updated once we ended the paragraph (and know if the para has the - * inTbl SPRM or not). - */ - sal_Int32 m_nTableDepth; /// Raw table cell depth. sal_Int32 m_nTableCellDepth; @@ -1182,7 +1211,7 @@ public: bool m_bIsActualParagraphFramed; std::deque<css::uno::Any> m_aStoredRedlines[StoredRedlines::NONE]; - bool IsParaWithInlineObject() const { return m_bParaWithInlineObject; } + bool IsParaWithInlineObject() const { return m_StreamStateStack.top().bParaWithInlineObject; } css::uno::Reference< css::embed::XStorage > m_xDocumentStorage; @@ -1209,17 +1238,9 @@ private: // Start a new index section; if needed, finish current paragraph css::uno::Reference<css::beans::XPropertySet> StartIndexSectionChecked(const OUString& sServiceName); std::vector<css::uno::Reference< css::drawing::XShape > > m_vTextFramesForChaining ; - /// Current paragraph had at least one field in it. - bool m_bParaHadField; - bool m_bSaveParaHadField; css::uno::Reference<css::beans::XPropertySet> m_xPreviousParagraph; /// Current paragraph has automatic before spacing. bool m_bParaAutoBefore; - /// Current paragraph in a table is first paragraph of a cell - bool m_bFirstParagraphInCell; - bool m_bSaveFirstParagraphInCell; - /// Current paragraph had at least one inline object in it. - bool m_bParaWithInlineObject; /// SAXException was seen so document will be abandoned bool m_bSaxError; diff --git a/writerfilter/source/dmapper/GraphicImport.cxx b/writerfilter/source/dmapper/GraphicImport.cxx index 63330c477c..eebd1b8228 100644 --- a/writerfilter/source/dmapper/GraphicImport.cxx +++ b/writerfilter/source/dmapper/GraphicImport.cxx @@ -1850,6 +1850,13 @@ uno::Reference<text::XTextContent> GraphicImport::createGraphicObject(uno::Refer m_pImpl->m_nLeftPosition = 0; } + if (m_pImpl->m_nVertRelation == text::RelOrientation::TEXT_LINE) + { + // Word's "line" is "below the bottom of the line", our TEXT_LINE is + // "towards top, from the bottom of the line", so invert the vertical position. + m_pImpl->m_nTopPosition *= -1; + } + m_pImpl->applyPosition(xGraphicObjectProperties); m_pImpl->applyRelativePosition(xGraphicObjectProperties); if( !m_pImpl->m_bOpaque ) diff --git a/writerfilter/source/dmapper/PropertyIds.cxx b/writerfilter/source/dmapper/PropertyIds.cxx index 01cf1203d3..b8b4efc062 100644 --- a/writerfilter/source/dmapper/PropertyIds.cxx +++ b/writerfilter/source/dmapper/PropertyIds.cxx @@ -383,6 +383,7 @@ namespace { PROP_CURSOR_NOT_IGNORE_TABLES_IN_HF, u"CursorNotIgnoreTables"}, { PROP_PARA_CONNECT_BORDERS, u"ParaIsConnectBorder"}, { PROP_DECORATIVE, u"Decorative"}, + { PROP_PAPER_TRAY, u"PrinterPaperTray"}, }); } // end anonymous ns diff --git a/writerfilter/source/dmapper/PropertyIds.hxx b/writerfilter/source/dmapper/PropertyIds.hxx index 83d05d81c8..b39fcd24fa 100644 --- a/writerfilter/source/dmapper/PropertyIds.hxx +++ b/writerfilter/source/dmapper/PropertyIds.hxx @@ -262,6 +262,7 @@ enum PropertyIds ,PROP_PARA_TOP_MARGIN ,PROP_PARA_VERT_ALIGNMENT ,PROP_PARA_WIDOWS + ,PROP_PAPER_TRAY ,PROP_PARENT_NUMBERING ,PROP_POSITION_AND_SPACE_MODE ,PROP_POSITION_PROTECTED diff --git a/writerfilter/source/dmapper/PropertyMap.cxx b/writerfilter/source/dmapper/PropertyMap.cxx index 9f9269b734..9c3e031e40 100644 --- a/writerfilter/source/dmapper/PropertyMap.cxx +++ b/writerfilter/source/dmapper/PropertyMap.cxx @@ -429,6 +429,8 @@ SectionPropertyMap::SectionPropertyMap( bool bIsFirstSection ) , m_nLnc(NS_ooxml::LN_Value_ST_LineNumberRestart_newPage) , m_ndxaLnn( 0 ) , m_nLnnMin( 0 ) + , m_nPaperSourceFirst( 0 ) + , m_nPaperSourceOther( 0 ) , m_bDynamicHeightTop( true ) , m_bDynamicHeightBottom( true ) { @@ -566,6 +568,12 @@ void SectionPropertyMap::setHeaderFooterProperties(DomainMapper_Impl& rDM_Impl) m_aPageStyle->setPropertyValue(getPropertyName(PROP_HEADER_IS_SHARED), uno::Any(!bEvenAndOdd)); m_aPageStyle->setPropertyValue(getPropertyName(PROP_FOOTER_IS_SHARED), uno::Any(!bEvenAndOdd)); m_aPageStyle->setPropertyValue(getPropertyName(PROP_FIRST_IS_SHARED), uno::Any(!m_bTitlePage)); + + bool bHadFirstHeader = m_bHadFirstHeader && m_bTitlePage; + if (bHasHeader && !bHadFirstHeader && !m_bHadLeftHeader && !m_bHadRightHeader) + { + m_aPageStyle->setPropertyValue(sHeaderIsOn, uno::Any(false)); + } } void SectionPropertyMap::SetBorder( BorderPosition ePos, sal_Int32 nLineDistance, const table::BorderLine2& rBorderLine, bool bShadow ) @@ -575,6 +583,27 @@ void SectionPropertyMap::SetBorder( BorderPosition ePos, sal_Int32 nLineDistance m_bBorderShadows[ePos] = bShadow; } +void SectionPropertyMap::ApplyPaperSource(DomainMapper_Impl& rDM_Impl) +{ + uno::Reference<beans::XPropertySet> xFirst; + // todo: negative spacing (from ww8par6.cxx) + if (!m_sPageStyleName.isEmpty()) + { + xFirst = GetPageStyle(rDM_Impl); + if ( xFirst.is() ) + try + { + //TODO: which of the two tray values needs to be set? first/other - the interfaces requires the name of the tray! + xFirst->setPropertyValue(getPropertyName(PROP_PAPER_TRAY), + uno::Any(m_nPaperSourceFirst)); + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("writerfilter", "Paper source not found"); + } + } +} + void SectionPropertyMap::ApplyBorderToPageStyles( DomainMapper_Impl& rDM_Impl, BorderApply /*eBorderApply*/, BorderOffsetFrom eOffsetFrom ) { @@ -1751,6 +1780,7 @@ void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl ) ApplyProperties_(xPageStyle); ApplyBorderToPageStyles( rDM_Impl, m_eBorderApply, m_eBorderOffsetFrom ); + ApplyPaperSource(rDM_Impl); try { diff --git a/writerfilter/source/dmapper/PropertyMap.hxx b/writerfilter/source/dmapper/PropertyMap.hxx index d6ecfb2e71..711ef47195 100644 --- a/writerfilter/source/dmapper/PropertyMap.hxx +++ b/writerfilter/source/dmapper/PropertyMap.hxx @@ -295,6 +295,9 @@ private: sal_Int32 m_ndxaLnn; sal_Int32 m_nLnnMin; + sal_Int32 m_nPaperSourceFirst; + sal_Int32 m_nPaperSourceOther; + bool m_bDynamicHeightTop; bool m_bDynamicHeightBottom; @@ -409,12 +412,14 @@ public: void SetdxaLnn( sal_Int32 nValue ) { m_ndxaLnn = nValue; } void SetLnnMin( sal_Int32 nValue ) { m_nLnnMin = nValue; } + void SetPaperSource(sal_Int32 first, sal_Int32 other) { m_nPaperSourceFirst = first; m_nPaperSourceOther = other;} + void addRelativeWidthShape( css::uno::Reference<css::drawing::XShape> xShape ) { m_xRelativeWidthShapes.push_back( xShape ); } // determine which style gets the borders void ApplyBorderToPageStyles( DomainMapper_Impl &rDM_Impl, BorderApply eBorderApply, BorderOffsetFrom eOffsetFrom ); - + void ApplyPaperSource(DomainMapper_Impl& rDM_Impl); void CloseSectionGroup( DomainMapper_Impl& rDM_Impl ); // Handling of margins, header and footer for any kind of sections breaks. void HandleMarginsHeaderFooter(DomainMapper_Impl& rDM_Impl); @@ -428,6 +433,9 @@ public: bool m_bLeftFooter = false; bool m_bRightHeader = false; bool m_bRightFooter = false; + bool m_bHadFirstHeader = false; + bool m_bHadLeftHeader = false; + bool m_bHadRightHeader = false; static void removeXTextContent(css::uno::Reference<css::text::XText> const& rxText); }; diff --git a/writerfilter/source/dmapper/SdtHelper.cxx b/writerfilter/source/dmapper/SdtHelper.cxx index 922ac5bea1..09e7903b5f 100644 --- a/writerfilter/source/dmapper/SdtHelper.cxx +++ b/writerfilter/source/dmapper/SdtHelper.cxx @@ -355,7 +355,7 @@ void SdtHelper::createPlainTextControl() try { bool bIsInTable = (m_rDM_Impl.hasTableManager() && m_rDM_Impl.getTableManager().isInTable()) - != (m_rDM_Impl.m_nTableDepth > 0) + != (0 < m_rDM_Impl.m_StreamStateStack.top().nTableDepth) && m_rDM_Impl.GetIsDummyParaAddedForTableInSection(); if (bIsInTable) xCrsr->goRight(1, false); @@ -459,7 +459,7 @@ void SdtHelper::createDateContentControl() // tdf#138093: Date selector reset, if placed inside table // Modified to XOR relationship and adding dummy paragraph conditions bool bIsInTable = (m_rDM_Impl.hasTableManager() && m_rDM_Impl.getTableManager().isInTable()) - != (m_rDM_Impl.m_nTableDepth > 0) + != (0 < m_rDM_Impl.m_StreamStateStack.top().nTableDepth) && m_rDM_Impl.GetIsDummyParaAddedForTableInSection(); if (bIsInTable) xCrsr->goRight(1, false); diff --git a/writerfilter/source/dmapper/SdtHelper.hxx b/writerfilter/source/dmapper/SdtHelper.hxx index 5db799bd1f..85b95a4881 100644 --- a/writerfilter/source/dmapper/SdtHelper.hxx +++ b/writerfilter/source/dmapper/SdtHelper.hxx @@ -171,6 +171,7 @@ public: void setDataBindingStoreItemID(const OUString& sValue) { m_sDataBindingStoreItemID = sValue; } const OUString& GetDataBindingStoreItemID() const { return m_sDataBindingStoreItemID; } + bool isFieldStartRangeSet() const { return m_xFieldStartRange.is(); } void setFieldStartRange(const css::uno::Reference<css::text::XTextRange>& xStartRange) { m_xFieldStartRange = xStartRange; diff --git a/writerfilter/source/dmapper/SettingsTable.cxx b/writerfilter/source/dmapper/SettingsTable.cxx index 43ef02f681..b168464206 100644 --- a/writerfilter/source/dmapper/SettingsTable.cxx +++ b/writerfilter/source/dmapper/SettingsTable.cxx @@ -778,6 +778,8 @@ bool SettingsTable::GetNoLeading() const bool SettingsTable::GetGutterAtTop() const { return m_pImpl->m_bGutterAtTop; } +bool SettingsTable::GetRecordChanges() const { return m_pImpl->m_bRecordChanges; } + }//namespace dmapper } //namespace writerfilter diff --git a/writerfilter/source/dmapper/SettingsTable.hxx b/writerfilter/source/dmapper/SettingsTable.hxx index a0af31bed6..471d15b7a9 100644 --- a/writerfilter/source/dmapper/SettingsTable.hxx +++ b/writerfilter/source/dmapper/SettingsTable.hxx @@ -97,6 +97,8 @@ public: const OUString& GetCurrentDatabaseDataSource() const; bool GetGutterAtTop() const; + bool GetRecordChanges() const; + private: // Properties virtual void lcl_attribute(Id Name, Value& val) override; diff --git a/writerfilter/source/filter/WriterFilter.cxx b/writerfilter/source/filter/WriterFilter.cxx index 1f97e8e70a..e7d32c4843 100644 --- a/writerfilter/source/filter/WriterFilter.cxx +++ b/writerfilter/source/filter/WriterFilter.cxx @@ -304,6 +304,7 @@ void WriterFilter::setTargetDocument(const uno::Reference<lang::XComponent>& xDo xSettings->setPropertyValue("UseOldNumbering", uno::Any(false)); xSettings->setPropertyValue("IgnoreFirstLineIndentInNumbering", uno::Any(false)); + xSettings->setPropertyValue(u"NoGapAfterNoteNumber"_ustr, uno::Any(true)); xSettings->setPropertyValue("DoNotResetParaAttrsForNumFont", uno::Any(false)); xSettings->setPropertyValue("UseFormerLineSpacing", uno::Any(false)); xSettings->setPropertyValue("AddParaSpacingToTableCells", uno::Any(true)); diff --git a/writerfilter/source/ooxml/OOXMLFastContextHandler.cxx b/writerfilter/source/ooxml/OOXMLFastContextHandler.cxx index cbc75c1407..ae69281a0c 100644 --- a/writerfilter/source/ooxml/OOXMLFastContextHandler.cxx +++ b/writerfilter/source/ooxml/OOXMLFastContextHandler.cxx @@ -1827,8 +1827,20 @@ void OOXMLFastContextHandlerShape::sendShape( Token_t Element ) uno::Reference<beans::XPropertySet> xShapePropSet(xShape, uno::UNO_QUERY); if (mnTableDepth > 0 && xShapePropSet.is() && mbIsVMLfound) //if we had a table { + bool bForceShapeIntoCell = mbAllowInCell; + // According to tdf#153909 and GraphicImport's LN_shape handling, + // through-anchored shapes should not force the shape into the cell + if (bForceShapeIntoCell) + { + text::WrapTextMode nSurround = text::WrapTextMode_NONE; + xShapePropSet->getPropertyValue("Surround") >>= nSurround; + sal_Int32 nHoriRelation = -1; + xShapePropSet->getPropertyValue("HoriOrientRelation") >>= nHoriRelation; + bForceShapeIntoCell = (nSurround != text::WrapTextMode_THROUGH) + || (nHoriRelation != text::RelOrientation::FRAME); + } xShapePropSet->setPropertyValue(dmapper::getPropertyName(dmapper::PROP_FOLLOW_TEXT_FLOW), - uno::Any(mbAllowInCell)); + uno::Any(bForceShapeIntoCell)); } // Notify the dmapper that the shape is ready to use if ( !bIsPicture ) diff --git a/writerfilter/source/ooxml/model.xml b/writerfilter/source/ooxml/model.xml index fd99a745e6..2c174743f9 100644 --- a/writerfilter/source/ooxml/model.xml +++ b/writerfilter/source/ooxml/model.xml @@ -17964,6 +17964,10 @@ <attribute name="footer" tokenid="ooxml:CT_PageMar_footer"/> <attribute name="gutter" tokenid="ooxml:CT_PageMar_gutter"/> </resource> + <resource name="CT_PaperSource" resource="Properties"> + <attribute name="first" tokenid="ooxml:CT_PaperSource_first"/> + <attribute name="other" tokenid="ooxml:CT_PaperSource_other"/> + </resource> <resource name="ST_PageBorderZOrder" resource="List"> <value tokenid="ooxml:Value_doc_ST_PageBorderZOrder_front">front</value> <value tokenid="ooxml:Value_doc_ST_PageBorderZOrder_back">back</value> diff --git a/writerfilter/source/rtftok/rtfdispatchsymbol.cxx b/writerfilter/source/rtftok/rtfdispatchsymbol.cxx index aa1360f6dc..0f01d79f5c 100644 --- a/writerfilter/source/rtftok/rtfdispatchsymbol.cxx +++ b/writerfilter/source/rtftok/rtfdispatchsymbol.cxx @@ -131,11 +131,8 @@ RTFError RTFDocumentImpl::dispatchSymbol(RTFKeyword nKeyword) break; case RTFKeyword::SECT: { - if (m_bNeedCr) - dispatchSymbol(RTFKeyword::PAR); - m_bHadSect = true; - if (m_bIgnoreNextContSectBreak) + if (m_bIgnoreNextContSectBreak || m_aStates.top().getFrame().hasProperties()) { // testContSectionPageBreak: need \par now dispatchSymbol(RTFKeyword::PAR); @@ -143,6 +140,10 @@ RTFError RTFDocumentImpl::dispatchSymbol(RTFKeyword nKeyword) } else { + if (m_bNeedCr) + { // tdf#158586 don't dispatch \par here, it eats deferred page breaks + setNeedPar(true); + } sectBreak(); if (m_nResetBreakOnSectBreak != RTFKeyword::invalid) { diff --git a/writerfilter/source/rtftok/rtfdispatchvalue.cxx b/writerfilter/source/rtftok/rtfdispatchvalue.cxx index 69157a9782..f699b0ed39 100644 --- a/writerfilter/source/rtftok/rtfdispatchvalue.cxx +++ b/writerfilter/source/rtftok/rtfdispatchvalue.cxx @@ -1342,6 +1342,18 @@ RTFError RTFDocumentImpl::dispatchValue(RTFKeyword nKeyword, int nParam) NS_ooxml::LN_EG_SectPrContents_pgSz, NS_ooxml::LN_CT_PageSz_w, pIntValue); break; + case RTFKeyword::BINFSXN: + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_paperSrc, + NS_ooxml::LN_CT_PaperSource_first, pIntValue); + break; + case RTFKeyword::BINSXN: + { + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_paperSrc, + NS_ooxml::LN_CT_PaperSource_other, pIntValue); + } + break; case RTFKeyword::MARGL: putNestedAttribute(m_aDefaultState.getSectionSprms(), NS_ooxml::LN_EG_SectPrContents_pgMar, NS_ooxml::LN_CT_PageMar_left, diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.cxx b/writerfilter/source/rtftok/rtfdocumentimpl.cxx index 1d0c2d7dde..e82674930b 100644 --- a/writerfilter/source/rtftok/rtfdocumentimpl.cxx +++ b/writerfilter/source/rtftok/rtfdocumentimpl.cxx @@ -653,6 +653,7 @@ void RTFDocumentImpl::runBreak() void RTFDocumentImpl::tableBreak() { + checkFirstRun(); // ooo113308-1.rtf has a header at offset 151084 that doesn't startParagraphGroup() without this runBreak(); Mapper().endParagraphGroup(); Mapper().startParagraphGroup(); @@ -671,7 +672,10 @@ void RTFDocumentImpl::parBreak() m_bHadPicture = false; // start new one - Mapper().startParagraphGroup(); + if (!m_bParAtEndOfSection) + { + Mapper().startParagraphGroup(); + } } void RTFDocumentImpl::sectBreak(bool bFinal) @@ -685,14 +689,26 @@ void RTFDocumentImpl::sectBreak(bool bFinal) // unless this is the end of the doc, we had nothing since the last section break and this is not a continuous one. // Also, when pasting, it's fine to not have any paragraph inside the document at all. if (m_bNeedPar && (!bFinal || m_bNeedSect || bContinuous) && !isSubstream() && m_bIsNewDoc) + { + m_bParAtEndOfSection = true; dispatchSymbol(RTFKeyword::PAR); + } // It's allowed to not have a non-table paragraph at the end of an RTF doc, add it now if required. if (m_bNeedFinalPar && bFinal) { dispatchFlag(RTFKeyword::PARD); + m_bParAtEndOfSection = true; dispatchSymbol(RTFKeyword::PAR); m_bNeedSect = bNeedSect; } + // testTdf148515, if RTF ends with \row, endParagraphGroup() must be called! + if (!m_bParAtEndOfSection || m_aStates.top().getCurrentBuffer()) + { + Mapper().endParagraphGroup(); // < top para context dies with page break + } + m_bParAtEndOfSection = false; + // paragraph properties are *done* now - only section properties following + while (!m_nHeaderFooterPositions.empty()) { std::pair<Id, std::size_t> aPair = m_nHeaderFooterPositions.front(); @@ -725,7 +741,6 @@ void RTFDocumentImpl::sectBreak(bool bFinal) // The trick is that we send properties of the previous section right now, which will be exactly what dmapper expects. Mapper().props(pProperties); - Mapper().endParagraphGroup(); // End Section if (!m_pSuperstream) @@ -2119,6 +2134,8 @@ RTFError RTFDocumentImpl::pushState() case Destination::FIELDRESULT: case Destination::SHAPETEXT: case Destination::FORMFIELD: + //TODO: if this is pushed then the font encoding is used which results in a broken command string + // if it is not pushed to NORMAL then it is not restored in time. case Destination::FIELDINSTRUCTION: case Destination::PICT: m_aStates.top().setDestination(Destination::NORMAL); @@ -2342,7 +2359,7 @@ RTFError RTFDocumentImpl::beforePopState(RTFParserState& rState) if (m_aStates.top().isFieldLocked()) singleChar(cFieldLock); - singleChar(cFieldSep); + singleChar(cFieldSep, true); } break; case Destination::FIELDRESULT: @@ -2569,7 +2586,7 @@ RTFError RTFDocumentImpl::beforePopState(RTFParserState& rState) str = OUString::Concat(field) + " \"" + str.replaceAll("\"", "\\\"") + "\""; singleChar(cFieldStart); Mapper().utext(str.getStr(), str.getLength()); - singleChar(cFieldSep); + singleChar(cFieldSep, true); // no result singleChar(cFieldEnd); } @@ -3654,8 +3671,15 @@ RTFError RTFDocumentImpl::popState() // \par means an empty paragraph at the end of footnotes/endnotes, but // not in case of other substreams, like headers. if (m_bNeedCr && m_nStreamType != NS_ooxml::LN_footnote - && m_nStreamType != NS_ooxml::LN_endnote && m_bIsNewDoc) + && m_nStreamType != NS_ooxml::LN_endnote) + { + if (!m_bIsNewDoc) + { + // Make sure all the paragraph settings are set, but do not add next paragraph + Mapper().markLastParagraph(); + } dispatchSymbol(RTFKeyword::PAR); + } if (m_bNeedSect) // may be set by dispatchSymbol above! sectBreak(true); else if (!m_pSuperstream) @@ -3782,8 +3806,11 @@ void RTFDocumentImpl::checkUnicode(bool bUnicode, bool bHex) if (bHex && !m_aHexBuffer.isEmpty()) { rtl_TextEncoding nEncoding = m_aStates.top().getCurrentEncoding(); - if (m_aStates.top().getDestination() == Destination::FONTENTRY - && m_aStates.top().getCurrentEncoding() == RTL_TEXTENCODING_SYMBOL) + if (nEncoding == RTL_TEXTENCODING_SYMBOL + && (m_aStates.top().getDestination() == Destination::FONTENTRY + || (m_aStates.size() > 1 + && m_aStates[m_aStates.size() - 2].getDestination() + == Destination::FIELDINSTRUCTION))) nEncoding = RTL_TEXTENCODING_MS_1252; OUString aString = OStringToOUString(m_aHexBuffer, nEncoding); m_aHexBuffer.setLength(0); diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.hxx b/writerfilter/source/rtftok/rtfdocumentimpl.hxx index eb50e3c7e0..f05f7d321c 100644 --- a/writerfilter/source/rtftok/rtfdocumentimpl.hxx +++ b/writerfilter/source/rtftok/rtfdocumentimpl.hxx @@ -852,6 +852,8 @@ private: bool m_bNeedPar; /// If set, an empty paragraph will be added at the end of the document. bool m_bNeedFinalPar; + /// a synthetic \par was dispatched at the end of the current section + bool m_bParAtEndOfSection = false; /// The list table and list override table combined. RTFSprms m_aListTableSprms; /// Maps between listoverridetable and listtable indexes. diff --git a/writerfilter/source/rtftok/rtfsprm.cxx b/writerfilter/source/rtftok/rtfsprm.cxx index 5d57348f20..148d39c2e4 100644 --- a/writerfilter/source/rtftok/rtfsprm.cxx +++ b/writerfilter/source/rtftok/rtfsprm.cxx @@ -437,12 +437,22 @@ RTFSprms RTFSprms::cloneAndDeduplicate(RTFSprms& rReference, Id const nStyleType return ret; } -bool RTFSprms::equals(const RTFValue& rOther) const +bool RTFSprms::equals(const RTFSprms& rOther) const { - return std::all_of(m_pSprms->cbegin(), m_pSprms->cend(), - [&](const std::pair<Id, RTFValue::Pointer_t>& raPair) -> bool { - return raPair.second->equals(rOther); - }); + auto it1 = m_pSprms->cbegin(); + auto it1End = m_pSprms->cend(); + auto it2 = rOther.m_pSprms->cbegin(); + auto it2End = rOther.m_pSprms->cend(); + while (it1 != it1End && it2 != it2End) + { + if (it1->first != it2->first) + return false; + if (!it1->second->equals(*it2->second)) + return false; + ++it1; + ++it2; + } + return it1 == it1End && it2 == it2End; } void RTFSprms::ensureCopyBeforeWrite() diff --git a/writerfilter/source/rtftok/rtfsprm.hxx b/writerfilter/source/rtftok/rtfsprm.hxx index 9f3bbd78b3..132a2bbcbe 100644 --- a/writerfilter/source/rtftok/rtfsprm.hxx +++ b/writerfilter/source/rtftok/rtfsprm.hxx @@ -73,7 +73,7 @@ public: Iterator_t begin() { return m_pSprms->begin(); } Iterator_t end() { return m_pSprms->end(); } void clear(); - bool equals(const RTFValue& rOther) const; + bool equals(const RTFSprms& rOther) const; private: void ensureCopyBeforeWrite(); diff --git a/writerfilter/source/rtftok/rtfvalue.cxx b/writerfilter/source/rtftok/rtfvalue.cxx index 373fb7521b..7a9137f5db 100644 --- a/writerfilter/source/rtftok/rtfvalue.cxx +++ b/writerfilter/source/rtftok/rtfvalue.cxx @@ -172,7 +172,7 @@ bool RTFValue::equals(const RTFValue& rOther) const { if (m_pAttributes->size() != rOther.m_pAttributes->size()) return false; - if (!m_pAttributes->equals(rOther)) + if (!m_pAttributes->equals(*rOther.m_pAttributes)) return false; } else if (m_pAttributes && m_pAttributes->size()) @@ -188,7 +188,7 @@ bool RTFValue::equals(const RTFValue& rOther) const { if (m_pSprms->size() != rOther.m_pSprms->size()) return false; - if (!m_pSprms->equals(rOther)) + if (!m_pSprms->equals(*rOther.m_pSprms)) return false; } else if (m_pSprms && m_pSprms->size()) |