diff --git a/include/xmloff/txtparae.hxx b/include/xmloff/txtparae.hxx index c003909795dd..c8772907045b 100644 --- a/include/xmloff/txtparae.hxx +++ b/include/xmloff/txtparae.hxx @@ -114,6 +114,8 @@ class XMLOFF_DLLPUBLIC XMLTextParagraphExport : public XMLStyleExport struct DocumentListNodes; std::unique_ptr mpDocumentListNodes; + std::vector maDocumentNodeOrder; + bool bInDocumentNodeOrderCollection = false; o3tl::sorted_vector> maFrameRecurseGuard; o3tl::sorted_vector> maShapeRecurseGuard; @@ -472,9 +474,7 @@ public: exportText( rText, rBaseSection, true, bIsProgress, true/*bExportParagraph*/ ); } - // It the model implements the xAutoStylesSupplier interface, the automatic - // styles can exported without iterating over the text portions - void collectTextAutoStylesOptimized( bool bIsProgress ); + void collectTextAutoStylesAndNodeExportOrder(bool bIsProgress); // This method exports all automatic styles that have been collected. void exportTextAutoStyles(); @@ -540,6 +540,7 @@ public: void PopTextListsHelper(); private: + void RecordNodeIndex(const css::uno::Reference& xTextContent); bool ShouldSkipListId(const css::uno::Reference& xTextContent); bool ExportListId() const; diff --git a/sw/qa/extras/htmlexport/xhtmlexport.cxx b/sw/qa/extras/htmlexport/xhtmlexport.cxx index 8527e66e024c..70fa1738d290 100644 --- a/sw/qa/extras/htmlexport/xhtmlexport.cxx +++ b/sw/qa/extras/htmlexport/xhtmlexport.cxx @@ -178,9 +178,9 @@ CPPUNIT_TEST_FIXTURE(XHtmlExportTest, testTdf66305) sal_uInt64 nLength = pStream->TellEnd(); OString aStream(read_uInt8s_ToOString(*pStream, nLength)); CPPUNIT_ASSERT( - aStream.indexOf("

Introduction

 

") + "class=\"paragraph-P6\"> 

") != -1); } diff --git a/sw/qa/extras/odfexport/data/tdf160253_ordinary_numbering.fodt b/sw/qa/extras/odfexport/data/tdf160253_ordinary_numbering.fodt new file mode 100644 index 000000000000..ca5c1128d1c0 --- /dev/null +++ b/sw/qa/extras/odfexport/data/tdf160253_ordinary_numbering.fodt @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + a + + + b + + + ci + + + xyz + + + + + d + + + + + \ No newline at end of file diff --git a/sw/qa/extras/odfexport/data/tdf160253_outline_numbering.fodt b/sw/qa/extras/odfexport/data/tdf160253_outline_numbering.fodt new file mode 100644 index 000000000000..2eddd6dd4593 --- /dev/null +++ b/sw/qa/extras/odfexport/data/tdf160253_outline_numbering.fodt @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + foo + xyzi + abc + bar + + + \ No newline at end of file diff --git a/sw/qa/extras/odfexport/odfexport2.cxx b/sw/qa/extras/odfexport/odfexport2.cxx index 4523e0551e18..4432d9eb1623 100644 --- a/sw/qa/extras/odfexport/odfexport2.cxx +++ b/sw/qa/extras/odfexport/odfexport2.cxx @@ -115,10 +115,14 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf106733) CPPUNIT_ASSERT_EQUAL(1, getPages()); xmlDocUniquePtr pXmlDoc = parseExport("content.xml"); + OUString autostyle = getXPath(pXmlDoc, "//office:body/office:text/text:p[2]/text:span"_ostr, + "style-name"_ostr); + OString autostyle_span_xpath = "//style:style[@style:name='" + autostyle.toUtf8() + "']"; + // keep fo:hyphenate="false" in direct formatting assertXPath( pXmlDoc, - "//style:style[@style:name='T3']/style:text-properties"_ostr, + autostyle_span_xpath + "/style:text-properties", "hyphenate"_ostr, "false"); // keep fo:hyphenate="false" in character style @@ -1250,9 +1254,13 @@ CPPUNIT_TEST_FIXTURE(Test, testParagraphMarkerMarkupRoundtrip) loadAndReload("ParagraphMarkerMarkup.fodt"); // Test that the markup stays at save-and-reload xmlDocUniquePtr pXmlDoc = parseExport("content.xml"); - assertXPath(pXmlDoc, "/office:document-content/office:body/office:text/text:p"_ostr, "marker-style-name"_ostr, "T2"); - assertXPath(pXmlDoc, "/office:document-content/office:automatic-styles/style:style[@style:name='T2']/style:text-properties"_ostr, "font-size"_ostr, "9pt"); - assertXPath(pXmlDoc, "/office:document-content/office:automatic-styles/style:style[@style:name='T2']/style:text-properties"_ostr, "color"_ostr, "#ff0000"); + OUString autostyle + = getXPath(pXmlDoc, "//office:body/office:text/text:p"_ostr, "marker-style-name"_ostr); + OString style_text_properties + = "/office:document-content/office:automatic-styles/style:style[@style:name='" + + autostyle.toUtf8() + "']/style:text-properties"; + assertXPath(pXmlDoc, style_text_properties, "font-size"_ostr, "9pt"); + assertXPath(pXmlDoc, style_text_properties, "color"_ostr, "#ff0000"); } CPPUNIT_TEST_FIXTURE(Test, testCommentStyles) @@ -1529,6 +1537,46 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf160700) assertXPath(pXmlDoc, "//office:text/text:list/text:list-item/text:p/text:bookmark"_ostr); } +CPPUNIT_TEST_FIXTURE(Test, testTdf160253_ordinary_numbering) +{ + // Given a document with a list, and an out-of-the-list paragraph in the middle, having an + // endnote, which has a paragraph in another list. + // Before the fix, this already failed with + // Error: "list2916587379" is referenced by an IDREF, but not defined. + loadAndReload("tdf160253_ordinary_numbering.fodt"); + + // Make sure that the fourth paragraph has correct number - it was "1." before the fix + CPPUNIT_ASSERT_EQUAL(u"3."_ustr, + getProperty(getParagraph(4), u"ListLabelString"_ustr)); + + // Make sure that we emit an identifier for the first list, and refer to it in the continuation + xmlDocUniquePtr pXmlDoc = parseExport("content.xml"); + // This failed before the fix, because 'xml:id' attribute wasn't emitted + OUString firstListId + = getXPath(pXmlDoc, "//office:body/office:text/text:list[1]"_ostr, "id"_ostr); + CPPUNIT_ASSERT(!firstListId.isEmpty()); + assertXPath(pXmlDoc, "//office:body/office:text/text:list[2]"_ostr, "continue-list"_ostr, + firstListId); +} + +CPPUNIT_TEST_FIXTURE(Test, testTdf160253_outline_numbering) +{ + // Given a document with an outline (chapter) numbering, and a paragraph in the middle, having + // an endnote, which has a paragraph in a list. + // Before the fix, this already failed with + // Error: "list2916587379" is referenced by an IDREF, but not defined. + loadAndReload("tdf160253_outline_numbering.fodt"); + + // Make sure that the third paragraph has correct number - it was "1" before the fix + CPPUNIT_ASSERT_EQUAL(u"2"_ustr, + getProperty(getParagraph(3), u"ListLabelString"_ustr)); + + // The difference with the ordinary numbering is that for outline numbering, the list element + // isn't really necessary. It is a TODO to fix the output, and not export the list. + // xmlDocUniquePtr pXmlDoc = parseExport("content.xml"); + // assertXPath(pXmlDoc, "//office:body/office:text/text:list"_ostr, 0); +} + } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/core/text/txtfld.cxx b/sw/source/core/text/txtfld.cxx index 55e439096f3d..e522f4e08929 100644 --- a/sw/source/core/text/txtfld.cxx +++ b/sw/source/core/text/txtfld.cxx @@ -657,28 +657,6 @@ SwNumberPortion *SwTextFormatter::NewNumberPortion( SwTextFormatInfo &rInf ) con // Build a new numbering font basing on the current paragraph font: std::unique_ptr pNumFnt(new SwFont( &rInf.GetCharAttr(), pIDSA )); - const SwTextNode& rTextNode = *rInf.GetTextFrame()->GetTextNodeForParaProps(); - if (const SwpHints* pHints = rTextNode.GetpSwpHints()) - { - // Also look for an empty character hint that sits at the paragraph end: - for (size_t i = 0; i < pHints->Count(); ++i) - { - const SwTextAttr* pHint = pHints->GetSortedByEnd(i); - if (pHint->Which() == RES_TXTATR_AUTOFMT && pHint->GetEnd() - && pHint->GetStart() == *pHint->GetEnd() - && pHint->GetStart() == rTextNode.GetText().getLength()) - { - std::shared_ptr pSet - = pHint->GetAutoFormat().GetStyleHandle(); - if (pSet) - { - pNumFnt->SetDiffFnt(pSet.get(), pIDSA); - break; - } - } - } - } - // #i53199# if ( !pIDSA->get(DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT) ) { diff --git a/sw/source/filter/ww8/ww8par.cxx b/sw/source/filter/ww8/ww8par.cxx index a329b73ca7fb..bbdc70048c01 100644 --- a/sw/source/filter/ww8/ww8par.cxx +++ b/sw/source/filter/ww8/ww8par.cxx @@ -2477,7 +2477,7 @@ void wwSectionManager::SetHdFt(wwSection const &rSection, int nSect, } -void SwWW8ImplReader::AppendTextNode(SwPosition& rPos) +void SwWW8ImplReader::FinalizeTextNode(SwPosition& rPos, bool bAddNew) { SwTextNode* pText = m_pPaM->GetPointNode().GetTextNode(); @@ -2564,7 +2564,8 @@ void SwWW8ImplReader::AppendTextNode(SwPosition& rPos) m_bFirstPara = false; - m_rDoc.getIDocumentContentOperations().AppendTextNode(rPos); + if (bAddNew) + m_rDoc.getIDocumentContentOperations().AppendTextNode(rPos); // We can flush all anchored graphics at the end of a paragraph. m_xAnchorStck->Flush(); @@ -3480,13 +3481,13 @@ void SwWW8ImplReader::simpleAddTextToParagraph(std::u16string_view aAddString) else { m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(0, nCharsLeft)); - AppendTextNode(*m_pPaM->GetPoint()); + FinalizeTextNode(*m_pPaM->GetPoint()); m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString.copy(nCharsLeft)); } } else { - AppendTextNode(*m_pPaM->GetPoint()); + FinalizeTextNode(*m_pPaM->GetPoint()); m_rDoc.getIDocumentContentOperations().InsertString(*m_pPaM, addString); } @@ -3557,7 +3558,7 @@ bool SwWW8ImplReader::HandlePageBreakChar() && (m_bFirstPara || m_bFirstParaOfPage)) { IsTemp = false; - AppendTextNode(*m_pPaM->GetPoint()); + FinalizeTextNode(*m_pPaM->GetPoint()); pTemp->SetAttr(*GetDfltAttr(RES_PARATR_NUMRULE)); } @@ -3636,7 +3637,7 @@ bool SwWW8ImplReader::ReadChar(tools::Long nPosCp, tools::Long nCpOfs) // Always insert a txtnode for a column break, e.g. ## SwContentNode *pCntNd=m_pPaM->GetPointContentNode(); if (pCntNd!=nullptr && pCntNd->Len()>0) // if par is empty not break is needed - AppendTextNode(*m_pPaM->GetPoint()); + FinalizeTextNode(*m_pPaM->GetPoint()); m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SvxFormatBreakItem(SvxBreak::ColumnBefore, RES_BREAK)); } break; @@ -4105,7 +4106,7 @@ bool SwWW8ImplReader::ReadText(WW8_CP nStartCp, WW8_CP nTextLen, ManTypes nType) } if (bSplit) { - AppendTextNode(*m_pPaM->GetPoint()); + FinalizeTextNode(*m_pPaM->GetPoint()); } } @@ -4220,7 +4221,7 @@ bool SwWW8ImplReader::ReadText(WW8_CP nStartCp, WW8_CP nTextLen, ManTypes nType) // to insert a text node. if (!bStartLine && !m_xAnchorStck->empty()) { - AppendTextNode(*m_pPaM->GetPoint()); + FinalizeTextNode(*m_pPaM->GetPoint()); } m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK)); @@ -4233,7 +4234,7 @@ bool SwWW8ImplReader::ReadText(WW8_CP nStartCp, WW8_CP nTextLen, ManTypes nType) m_xPreviousNode.reset(); if (m_pPaM->GetPoint()->GetContentIndex()) - AppendTextNode(*m_pPaM->GetPoint()); + FinalizeTextNode(*m_pPaM->GetPoint()); if (!m_bInHyperlink) bJoined = JoinNode(*m_pPaM); diff --git a/sw/source/filter/ww8/ww8par.hxx b/sw/source/filter/ww8/ww8par.hxx index caaafc2b122a..6f0c260f1966 100644 --- a/sw/source/filter/ww8/ww8par.hxx +++ b/sw/source/filter/ww8/ww8par.hxx @@ -1421,7 +1421,7 @@ private: bool StyleExists(unsigned int nColl) const { return (nColl < m_vColl.size()); } SwWW8StyInf *GetStyle(sal_uInt16 nColl) const; - void AppendTextNode(SwPosition& rPos); + void FinalizeTextNode(SwPosition& rPos, bool bAddNew = true); void Read_HdFt(int nSect, const SwPageDesc *pPrev, const wwSection &rSection); diff --git a/sw/source/filter/ww8/ww8par2.cxx b/sw/source/filter/ww8/ww8par2.cxx index 061144045b12..c1e80efd2d38 100644 --- a/sw/source/filter/ww8/ww8par2.cxx +++ b/sw/source/filter/ww8/ww8par2.cxx @@ -2402,7 +2402,7 @@ void WW8TabDesc::CreateSwTable() } if (bInsNode) - m_pIo->AppendTextNode(*pPoint); + m_pIo->FinalizeTextNode(*pPoint); m_xTmpPos = m_pIo->m_rDoc.CreateUnoCursor(*m_pIo->m_pPaM->GetPoint()); @@ -3497,6 +3497,8 @@ bool SwWW8ImplReader::StartTable(WW8_CP nStartCp) void SwWW8ImplReader::TabCellEnd() { + FinalizeTextNode(*m_pPaM->GetPoint(), false); + if (m_nInTable && m_xTableDesc) m_xTableDesc->TableCellEnd(); diff --git a/sw/source/filter/ww8/ww8par5.cxx b/sw/source/filter/ww8/ww8par5.cxx index 47cb7e788223..f34f5996caa9 100644 --- a/sw/source/filter/ww8/ww8par5.cxx +++ b/sw/source/filter/ww8/ww8par5.cxx @@ -3477,7 +3477,7 @@ eF_ResT SwWW8ImplReader::Read_F_Tox( WW8FieldDesc* pF, OUString& rStr ) } if (m_pPaM->GetPoint()->GetContentIndex()) - AppendTextNode(*m_pPaM->GetPoint()); + FinalizeTextNode(*m_pPaM->GetPoint()); const SwPosition* pPos = m_pPaM->GetPoint(); diff --git a/sw/source/filter/ww8/ww8par6.cxx b/sw/source/filter/ww8/ww8par6.cxx index 6b33ac0d5bd6..bc89199c4904 100644 --- a/sw/source/filter/ww8/ww8par6.cxx +++ b/sw/source/filter/ww8/ww8par6.cxx @@ -881,7 +881,7 @@ void wwSectionManager::CreateSep(const tools::Long nTextPos) if( txtNode->Len() == 0 ) insert = false; if( insert ) - mrReader.AppendTextNode(*mrReader.m_pPaM->GetPoint()); + mrReader.FinalizeTextNode(*mrReader.m_pPaM->GetPoint()); } ww::WordVersion eVer = mrReader.GetFib().GetFIBVersion(); @@ -2528,7 +2528,7 @@ bool SwWW8ImplReader::StartApo(const ApoTestResults &rApo, const WW8_TablePos *p { // The two fly frames would have the same anchor position, leading to // potentially overlapping text, prevent that. - AppendTextNode(*pPoint); + FinalizeTextNode(*pPoint); } } } diff --git a/sw/source/filter/xml/xmlfmte.cxx b/sw/source/filter/xml/xmlfmte.cxx index fc8932e8bb2f..d64810724cf8 100644 --- a/sw/source/filter/xml/xmlfmte.cxx +++ b/sw/source/filter/xml/xmlfmte.cxx @@ -247,7 +247,7 @@ void SwXMLExport::collectAutoStyles() GetFormExport()->examineForms(xPage); } - GetTextParagraphExport()->collectTextAutoStylesOptimized( m_bShowProgress ); + GetTextParagraphExport()->collectTextAutoStylesAndNodeExportOrder(m_bShowProgress); } mbAutoStylesCollected = true; diff --git a/xmloff/source/text/txtparae.cxx b/xmloff/source/text/txtparae.cxx index 6153fb09d7a8..786539812341 100644 --- a/xmloff/source/text/txtparae.cxx +++ b/xmloff/source/text/txtparae.cxx @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -1331,12 +1332,14 @@ struct XMLTextParagraphExport::DocumentListNodes { struct NodeData { + std::ptrdiff_t order; sal_Int32 index; // see SwNode::GetIndex and SwNodeOffset sal_uInt64 style_id; // actually a pointer to NumRule OUString list_id; }; std::vector docListNodes; - DocumentListNodes(const css::uno::Reference& xModel) + DocumentListNodes(const css::uno::Reference& xModel, + const std::vector& aDocumentNodeOrder) { // Sequence of nodes, each of them represented by three-element sequence, // corresponding to NodeData members @@ -1358,13 +1361,18 @@ struct XMLTextParagraphExport::DocumentListNodes for (const auto& node : nodes) { assert(node.getLength() == 3); - docListNodes.push_back({ .index = node[0].get(), + sal_Int32 nodeIndex = node[0].get(); + auto nodeOrder = std::distance( + aDocumentNodeOrder.begin(), + std::find(aDocumentNodeOrder.begin(), aDocumentNodeOrder.end(), nodeIndex)); + docListNodes.push_back({ .order = nodeOrder, + .index = nodeIndex, .style_id = node[1].get(), .list_id = node[2].get() }); } std::sort(docListNodes.begin(), docListNodes.end(), - [](const NodeData& lhs, const NodeData& rhs) { return lhs.index < rhs.index; }); + [](const NodeData& lhs, const NodeData& rhs) { return lhs.order < rhs.order; }); } bool ShouldSkipListId(const Reference& xTextContent) const { @@ -1385,10 +1393,9 @@ struct XMLTextParagraphExport::DocumentListNodes return false; } - auto it = std::lower_bound(docListNodes.begin(), docListNodes.end(), index, - [](const NodeData& lhs, sal_Int32 rhs) - { return lhs.index < rhs; }); - if (it == docListNodes.end() || it->index != index) + auto it = std::find_if(docListNodes.begin(), docListNodes.end(), + [index](const NodeData& el) { return el.index == index; }); + if (it == docListNodes.end()) return false; // We need to write the id, when there will be continuation of the list either with @@ -1616,9 +1623,7 @@ const enum XMLTokenEnum lcl_XmlReferenceElements[] = { const enum XMLTokenEnum lcl_XmlBookmarkElements[] = { XML_BOOKMARK, XML_BOOKMARK_START, XML_BOOKMARK_END }; -// This function replaces the text portion iteration during auto style -// collection. -void XMLTextParagraphExport::collectTextAutoStylesOptimized( bool bIsProgress ) +void XMLTextParagraphExport::collectTextAutoStylesAndNodeExportOrder(bool bIsProgress) { GetExport().GetShapeExport(); // make sure the graphics styles family is added @@ -1628,60 +1633,11 @@ void XMLTextParagraphExport::collectTextAutoStylesOptimized( bool bIsProgress ) const bool bAutoStyles = true; const bool bExportContent = false; - // Export AutoStyles: - Reference< XAutoStylesSupplier > xAutoStylesSupp( GetExport().GetModel(), UNO_QUERY ); - if ( xAutoStylesSupp.is() ) + if (auto xTextDocument = GetExport().GetModel().query()) { - Reference< XAutoStyles > xAutoStyleFamilies = xAutoStylesSupp->getAutoStyles(); - const auto collectFamily = [this, &xAutoStyleFamilies](const OUString& sName, - XmlStyleFamily nFamily) { - Any aAny = xAutoStyleFamilies->getByName( sName ); - Reference< XAutoStyleFamily > xAutoStyles = *o3tl::doAccess>(aAny); - Reference < XEnumeration > xAutoStylesEnum( xAutoStyles->createEnumeration() ); - - while ( xAutoStylesEnum->hasMoreElements() ) - { - aAny = xAutoStylesEnum->nextElement(); - Reference< XAutoStyle > xAutoStyle = *o3tl::doAccess>(aAny); - Reference < XPropertySet > xPSet( xAutoStyle, uno::UNO_QUERY ); - Add( nFamily, xPSet, {}, true ); - } - }; - collectFamily("CharacterStyles", XmlStyleFamily::TEXT_TEXT); - collectFamily("RubyStyles", XmlStyleFamily::TEXT_RUBY); - collectFamily("ParagraphStyles", XmlStyleFamily::TEXT_PARAGRAPH); - } - - // Export Field AutoStyles: - Reference< XTextFieldsSupplier > xTextFieldsSupp( GetExport().GetModel(), UNO_QUERY ); - if ( xTextFieldsSupp.is() ) - { - Reference< XEnumerationAccess > xTextFields = xTextFieldsSupp->getTextFields(); - Reference < XEnumeration > xTextFieldsEnum( xTextFields->createEnumeration() ); - - while ( xTextFieldsEnum->hasMoreElements() ) - { - Any aAny = xTextFieldsEnum->nextElement(); - Reference< XTextField > xTextField = *o3tl::doAccess>(aAny); - exportTextField( xTextField, bAutoStyles, bIsProgress, - !xAutoStylesSupp.is(), nullptr ); - try - { - Reference < XPropertySet > xSet( xTextField, UNO_QUERY ); - Reference < XText > xText; - Any a = xSet->getPropertyValue("TextRange"); - a >>= xText; - if ( xText.is() ) - { - exportText( xText, true, bIsProgress, bExportContent ); - GetExport().GetTextParagraphExport() - ->collectTextAutoStyles( xText ); - } - } - catch (Exception&) - { - } - } + bInDocumentNodeOrderCollection = true; + collectTextAutoStyles(xTextDocument->getText(), bIsProgress); + bInDocumentNodeOrderCollection = false; } // Export text frames: @@ -1728,47 +1684,11 @@ void XMLTextParagraphExport::collectTextAutoStylesOptimized( bool bIsProgress ) } } - sal_Int32 nCount; - // AutoStyles for sections - Reference< XTextSectionsSupplier > xSectionsSupp( GetExport().GetModel(), UNO_QUERY ); - if ( xSectionsSupp.is() ) - { - Reference< XIndexAccess > xSections( xSectionsSupp->getTextSections(), UNO_QUERY ); - if ( xSections.is() ) - { - nCount = xSections->getCount(); - for( sal_Int32 i = 0; i < nCount; ++i ) - { - Any aAny = xSections->getByIndex( i ); - Reference< XTextSection > xSection = *o3tl::doAccess>(aAny); - Reference < XPropertySet > xPSet( xSection, uno::UNO_QUERY ); - Add( XmlStyleFamily::TEXT_SECTION, xPSet ); - } - } - } - - // AutoStyles for tables (Note: suppress autostyle collection for paragraphs in exportTable) - Reference< XTextTablesSupplier > xTablesSupp( GetExport().GetModel(), UNO_QUERY ); - if ( xTablesSupp.is() ) - { - Reference< XIndexAccess > xTables( xTablesSupp->getTextTables(), UNO_QUERY ); - if ( xTables.is() ) - { - nCount = xTables->getCount(); - for( sal_Int32 i = 0; i < nCount; ++i ) - { - Any aAny = xTables->getByIndex( i ); - Reference< XTextTable > xTable = *o3tl::doAccess>(aAny); - exportTable( xTable, true, true ); - } - } - } - Reference< XNumberingRulesSupplier > xNumberingRulesSupp( GetExport().GetModel(), UNO_QUERY ); if ( xNumberingRulesSupp.is() ) { Reference< XIndexAccess > xNumberingRules = xNumberingRulesSupp->getNumberingRules(); - nCount = xNumberingRules->getCount(); + sal_Int32 nCount = xNumberingRules->getCount(); // Custom outline assignment lost after re-importing sxw (#i73361#) for( sal_Int32 i = 0; i < nCount; ++i ) { @@ -1894,14 +1814,36 @@ bool XMLTextParagraphExport::ExportListId() const && GetExport().getSaneDefaultVersion() >= SvtSaveOptions::ODFSVER_012; } +void XMLTextParagraphExport::RecordNodeIndex(const css::uno::Reference& xTextContent) +{ + if (!bInDocumentNodeOrderCollection) + return; + if (auto xPropSet = xTextContent.query()) + { + try + { + sal_Int32 index = 0; + // See SwXParagraph::Impl::GetPropertyValues_Impl + xPropSet->getPropertyValue("ODFExport_NodeIndex") >>= index; + assert(std::find(maDocumentNodeOrder.begin(), maDocumentNodeOrder.end(), index) + == maDocumentNodeOrder.end()); + maDocumentNodeOrder.push_back(index); + } + catch (css::beans::UnknownPropertyException&) + { + // That's absolutely fine! + } + } +} + bool XMLTextParagraphExport::ShouldSkipListId(const Reference& xTextContent) { if (!mpDocumentListNodes) { if (ExportListId()) - mpDocumentListNodes.reset(new DocumentListNodes(GetExport().GetModel())); + mpDocumentListNodes.reset(new DocumentListNodes(GetExport().GetModel(), maDocumentNodeOrder)); else - mpDocumentListNodes.reset(new DocumentListNodes({})); + mpDocumentListNodes.reset(new DocumentListNodes({}, {})); } return mpDocumentListNodes->ShouldSkipListId(xTextContent); @@ -1952,6 +1894,7 @@ void XMLTextParagraphExport::exportTextContentEnumeration( { if( bAutoStyles ) { + RecordNodeIndex(xTxtCntnt); exportListAndSectionChange( xCurrentTextSection, xTxtCntnt, aPrevNumInfo, aNextNumInfo, bAutoStyles ); @@ -2323,7 +2266,6 @@ void XMLTextParagraphExport::exportParagraph( Reference < XEnumerationAccess > xEA( rTextContent, UNO_QUERY ); Reference < XEnumeration > xTextEnum = xEA->createEnumeration(); - const bool bHasPortions = xTextEnum.is(); Reference < XEnumeration> xContentEnum; Reference < XContentEnumerationAccess > xCEA( rTextContent, UNO_QUERY ); @@ -2357,22 +2299,10 @@ void XMLTextParagraphExport::exportParagraph( bool bPrevCharIsSpace(true); // true because whitespace at start is ignored - if( bAutoStyles ) - { - if( bHasContentEnum ) - exportTextContentEnumeration( - xContentEnum, bAutoStyles, xSection, - bIsProgress ); - if ( bHasPortions ) - { - exportTextRangeEnumeration(xTextEnum, bAutoStyles, bIsProgress, bPrevCharIsSpace); - } - } - else { enum XMLTokenEnum eElem = 0 < nOutlineLevel ? XML_H : XML_P; - SvXMLElementExport aElem( GetExport(), eExtensionNS == TextPNS::EXTENSION ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_TEXT, eElem, + SvXMLElementExport aElem( GetExport(), !bAutoStyles, eExtensionNS == TextPNS::EXTENSION ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_TEXT, eElem, true, false ); if( bHasContentEnum ) { diff --git a/xmloff/source/text/txtparai.cxx b/xmloff/source/text/txtparai.cxx index 94cef85739ef..f2124bf068ed 100644 --- a/xmloff/source/text/txtparai.cxx +++ b/xmloff/source/text/txtparai.cxx @@ -1789,6 +1789,46 @@ void XMLParaContext::endFastElement(sal_Int32 ) true, mbOutlineContentVisible); + bool bEmptyHints = false; + XMLHint_Impl* pMarkerStyleHint = nullptr; + if (m_xHints) + { + uno::Reference xCompare(xTxtImport->GetText(), uno::UNO_QUERY); + if (xCompare.is()) + { + try + { + for (const auto& pHint : m_xHints->GetHints()) + { + if (xCompare->compareRegionStarts(pHint->GetStart(), pHint->GetEnd()) == 0) + { + bEmptyHints = true; + + // Is this the trailing empty span, defining the paragraph mark properties? + // Convert it to the marker style, for backward compatibility with documents + // created between commits 6249858a8972aef077e0249bd93cfe8f01bce4d6 and + // 1a88efa8e02a6d765dab13c7110443bb9e6acecf, where the trailing empty spans + // were used to store the marker formatting + if (!m_aMarkerStyleName.hasValue() + && xCompare->compareRegionStarts(pHint->GetStart(), xEnd) == 0) + { + if (auto pStyle = GetImport().GetTextImport()->FindAutoCharStyle( + static_cast(pHint.get())->GetStyleName())) + { + m_aMarkerStyleName = pStyle->GetAutoName(); + pMarkerStyleHint = pHint.get(); + } + } + } + } + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.text", ""); + } + } + } + if (m_aMarkerStyleName.hasValue()) { if (auto xPropSet = xStart.query()) @@ -1850,26 +1890,7 @@ void XMLParaContext::endFastElement(sal_Int32 ) { bool bSetNoFormatAttr = false; uno::Reference xCursorProps(xAttrCursor, uno::UNO_QUERY); - int nEmptyHints = 0; - uno::Reference xCompare(xTxtImport->GetText(), uno::UNO_QUERY); - if (xCompare.is()) - { - try - { - for (const auto& pHint : m_xHints->GetHints()) - { - if (xCompare->compareRegionStarts(pHint->GetStart(), pHint->GetEnd()) == 0) - { - ++nEmptyHints; - } - } - } - catch (const uno::Exception&) - { - TOOLS_WARN_EXCEPTION("xmloff.text", ""); - } - } - if (nEmptyHints > 0 || m_aMarkerStyleName.hasValue()) + if (bEmptyHints || m_aMarkerStyleName.hasValue()) { // We have at least one empty hint, then make try to ask the cursor to not upgrade our character // attributes to paragraph-level formatting, which would lead to incorrect rendering. @@ -1888,6 +1909,7 @@ void XMLParaContext::endFastElement(sal_Int32 ) switch( pHint->GetType() ) { case XMLHintType::XML_HINT_STYLE: + if (pHint != pMarkerStyleHint) // already processed above { const OUString& rStyleName = static_cast(pHint)->GetStyleName();