tdf160017: make sure to emit the closing tags in correct order
This reimplements how the starts and ends of attributes are stored in
HTMLEndPosLst. Instead of a plain list, now it is a sorted map, with
positions as keys, and a vector of HTMLStartEndPos* as values.
In commit b94b1fe936ddc4a9b86fbeb9c9c6ab0fca52f0bc (CharBrd 9.1: HTML
filters, 2013-09-08), the character borders attributes started to be
set in a special order, in front of the position's other attributes,
to allow merging them. But that created a problem of knowing in which
order to close respective tags.
The change here sorts the closing tags for the current node only when
writing them. At this point, it is possible to consider the opening
positions correctly.
Change-Id: I466ffa1c0eb28874ded003035e0cf772e31585b3
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164325
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
diff --git a/sw/qa/extras/htmlexport/data/char_border_and_font_color.fodt b/sw/qa/extras/htmlexport/data/char_border_and_font_color.fodt
new file mode 100644
index 0000000..bda2ec6
--- /dev/null
+++ b/sw/qa/extras/htmlexport/data/char_border_and_font_color.fodt
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
<office:automatic-styles>
<style:style style:name="P1" style:family="paragraph">
<style:text-properties fo:color="#000000" loext:border="none"/>
</style:style>
</office:automatic-styles>
<office:body>
<office:text>
<text:p text:style-name="P1">foo</text:p>
</office:text>
</office:body>
</office:document>
\ No newline at end of file
diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx
index fcb6092..45eb061 100644
--- a/sw/qa/extras/htmlexport/htmlexport.cxx
+++ b/sw/qa/extras/htmlexport/htmlexport.cxx
@@ -3017,6 +3017,27 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_NoBrClearForImageWrap)
0);
}
CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_Tdf160017_spanClosingOrder)
{
// Given a document with a paragraph having explicit font color and character border properties:
createSwDoc("char_border_and_font_color.fodt");
// When exporting to reqif:
ExportToReqif();
// Without the fix, this would fail, because there was an extra closing </reqif-xhtml:span>
WrapReqifFromTempFile();
}
CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_Tdf160017_spanClosingOrder)
{
// Given a document with a paragraph having explicit font color and character border properties:
createSwDoc("char_border_and_font_color.fodt");
// When exporting to HTML:
ExportToHTML();
// Parse it as XML (strict!)
// Without the fix, this would fail, because span and font elements closed in wrong order
CPPUNIT_ASSERT(parseXml(maTempFile));
}
} // end of anonymous namespace
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/filter/html/css1atr.cxx b/sw/source/filter/html/css1atr.cxx
index a07b0c9..48badab 100644
--- a/sw/source/filter/html/css1atr.cxx
+++ b/sw/source/filter/html/css1atr.cxx
@@ -3301,14 +3301,16 @@ SwHTMLWriter& OutCSS1_SvxBox( SwHTMLWriter& rWrt, const SfxPoolItem& rHt )
if( rHt.Which() == RES_CHRATR_BOX )
{
constexpr std::string_view inline_block("inline-block");
if( rWrt.m_bTagOn )
{
// Inline-block to make the line height changing correspond to the character border
rWrt.OutCSS1_PropertyAscii(sCSS1_P_display, "inline-block");
rWrt.OutCSS1_PropertyAscii(sCSS1_P_display, inline_block);
}
else
{
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
if (!IgnorePropertyForReqIF(rWrt.mbReqIF, sCSS1_P_display, inline_block))
HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false );
return rWrt;
}
}
diff --git a/sw/source/filter/html/htmlatr.cxx b/sw/source/filter/html/htmlatr.cxx
index 9f67d1e..c880082 100644
--- a/sw/source/filter/html/htmlatr.cxx
+++ b/sw/source/filter/html/htmlatr.cxx
@@ -1058,7 +1058,7 @@ public:
HTMLStartEndPos( const SfxPoolItem& rItem, sal_Int32 nStt, sal_Int32 nE );
const SfxPoolItem* GetItem() const { return m_pItem.get(); }
const SfxPoolItem& GetItem() const { return *m_pItem; }
void SetStart(sal_Int32 nStt) { m_nStart = nStt; }
sal_Int32 GetStart() const { return m_nStart; }
@@ -1075,7 +1075,7 @@ HTMLStartEndPos::HTMLStartEndPos(const SfxPoolItem& rItem, sal_Int32 nStt, sal_I
, m_pItem(rItem.Clone())
{}
typedef std::vector<HTMLStartEndPos *> HTMLStartEndPositions;
typedef std::map<sal_Int32, std::vector<HTMLStartEndPos*>> HTMLStartEndPositions;
namespace {
@@ -1091,8 +1091,8 @@ enum HTMLOnOffState { HTML_NOT_SUPPORTED, // unsupported Attribute
class HTMLEndPosLst
{
HTMLStartEndPositions m_aStartLst; // list, sorted for start positions
HTMLStartEndPositions m_aEndLst; // list, sorted for end positions
HTMLStartEndPositions m_aStartLst; // list, each position's elements sorted by appearance order
HTMLStartEndPositions m_aEndLst; // list, no sort of elements in position
std::deque<sal_Int32> m_aScriptChgLst; // positions where script changes
// 0 is not contained in this list,
// but the text length
@@ -1110,8 +1110,7 @@ class HTMLEndPosLst
// Insert/remove a SttEndPos in/from the Start and End lists.
// The end position is known.
void InsertItem_( HTMLStartEndPos *pPos, HTMLStartEndPositions::size_type nEndPos );
void RemoveItem_( HTMLStartEndPositions::size_type nEndPos );
void InsertItem_(HTMLStartEndPos* pPos);
// determine the 'type' of the attribute
HTMLOnOffState GetHTMLItemState( const SfxPoolItem& rItem );
@@ -1125,8 +1124,7 @@ class HTMLEndPosLst
sal_Int32 nEndPos );
// adapt the end of a split item
void FixSplittedItem( HTMLStartEndPos *pPos, sal_Int32 nNewEnd,
HTMLStartEndPositions::size_type nStartPos );
void FixSplittedItem(HTMLStartEndPos* pPos, sal_Int32 nNewEnd);
// insert an attribute in the lists and, if necessary, split it
void InsertItem( const SfxPoolItem& rItem, sal_Int32 nStart,
@@ -1144,6 +1142,8 @@ class HTMLEndPosLst
const SwHTMLFormatInfo *GetFormatInfo( const SwFormat& rFormat,
SwHTMLFormatInfos& rFormatInfos );
void OutEndAttrs(SwHTMLWriter& rWrt, std::vector<HTMLStartEndPos*>& posItems);
public:
HTMLEndPosLst( SwDoc *pDoc, SwDoc* pTemplate, std::optional<Color> xDfltColor,
@@ -1169,36 +1169,46 @@ public:
bool IsHTMLMode(sal_uLong nMode) const { return (m_nHTMLMode & nMode) != 0; }
};
struct SortEnds
{
HTMLStartEndPositions& m_startList;
SortEnds(HTMLStartEndPositions& startList) : m_startList(startList) {}
bool operator()(const HTMLStartEndPos* p1, const HTMLStartEndPos* p2)
{
// if p1 start after p2, then it ends before
if (p1->GetStart() > p2->GetStart())
return true;
if (p1->GetStart() < p2->GetStart())
return false;
for (const auto p : m_startList[p1->GetStart()])
{
if (p == p1)
return false;
if (p == p2)
return true;
}
assert(!"Neither p1 nor p2 found in their start list");
return false;
}
};
#ifndef NDEBUG
bool IsEmpty(const HTMLStartEndPositions& l)
{
return std::find_if(l.begin(), l.end(), [](auto& i) { return !i.second.empty(); }) == l.end();
}
#endif
}
void HTMLEndPosLst::InsertItem_( HTMLStartEndPos *pPos, HTMLStartEndPositions::size_type nEndPos )
void HTMLEndPosLst::InsertItem_(HTMLStartEndPos* pPos)
{
// Insert the attribute in the Start list behind all attributes that
// were started before, or at the same position.
sal_Int32 nStart = pPos->GetStart();
HTMLStartEndPositions::size_type i {0};
// Character border attribute must be the first which is written out because of border merge.
auto& posItems1 = m_aStartLst[pPos->GetStart()];
auto it = pPos->GetItem().Which() == RES_CHRATR_BOX ? posItems1.begin() : posItems1.end();
posItems1.insert(it, pPos);
while (i < m_aStartLst.size() && m_aStartLst[i]->GetStart() <= nStart)
++i;
m_aStartLst.insert(m_aStartLst.begin() + i, pPos);
// the position in the End list was supplied
m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pPos);
}
void HTMLEndPosLst::RemoveItem_( HTMLStartEndPositions::size_type nEndPos )
{
HTMLStartEndPos* pPos = m_aEndLst[nEndPos];
// now, we are looking for it in the Start list
HTMLStartEndPositions::iterator it = std::find(m_aStartLst.begin(), m_aStartLst.end(), pPos);
OSL_ENSURE(it != m_aStartLst.end(), "Item not found in Start List!");
if (it != m_aStartLst.end())
m_aStartLst.erase(it);
m_aEndLst.erase(m_aEndLst.begin() + nEndPos);
delete pPos;
m_aEndLst[pPos->GetEnd()].push_back(pPos);
}
HTMLOnOffState HTMLEndPosLst::GetHTMLItemState( const SfxPoolItem& rItem )
@@ -1352,23 +1362,25 @@ HTMLOnOffState HTMLEndPosLst::GetHTMLItemState( const SfxPoolItem& rItem )
bool HTMLEndPosLst::ExistsOnTagItem( sal_uInt16 nWhich, sal_Int32 nPos )
{
for (auto pTest : m_aStartLst)
for (const auto& [startPos, items] : m_aStartLst)
{
if( pTest->GetStart() > nPos )
if (startPos > nPos)
{
// this attribute, and all attributes that follow, start later
break;
}
else if( pTest->GetEnd() > nPos )
for (const auto* pTest : items)
{
// the attribute starts before, or at, the current position and
// ends after it
const SfxPoolItem *pItem = pTest->GetItem();
if( pItem->Which() == nWhich &&
HTML_ON_VALUE == GetHTMLItemState(*pItem) )
if (pTest->GetEnd() > nPos)
{
// an OnTag attribute was found
return true;
// the attribute starts before, or at, the current position and ends after it
const SfxPoolItem& rItem = pTest->GetItem();
if (rItem.Which() == nWhich && HTML_ON_VALUE == GetHTMLItemState(rItem))
{
// an OnTag attribute was found
return true;
}
}
}
}
@@ -1386,24 +1398,17 @@ bool HTMLEndPosLst::ExistsOffTagItem( sal_uInt16 nWhich, sal_Int32 nStartPos,
return false;
}
for (auto pTest : m_aStartLst)
for (const auto* pTest : m_aStartLst[nStartPos])
{
if( pTest->GetStart() > nStartPos )
if (pTest->GetEnd() == nEndPos)
{
// this attribute, and all attributes that follow, start later
break;
}
else if( pTest->GetStart()==nStartPos &&
pTest->GetEnd()==nEndPos )
{
// the attribute starts before or at the current position and
// ends after it
const SfxPoolItem *pItem = pTest->GetItem();
sal_uInt16 nTstWhich = pItem->Which();
// the attribute starts before or at the current position and ends after it
const SfxPoolItem& rItem = pTest->GetItem();
sal_uInt16 nTstWhich = rItem.Which();
if( (nTstWhich == RES_CHRATR_CROSSEDOUT ||
nTstWhich == RES_CHRATR_UNDERLINE ||
nTstWhich == RES_CHRATR_BLINK) &&
HTML_OFF_VALUE == GetHTMLItemState(*pItem) )
HTML_OFF_VALUE == GetHTMLItemState(rItem) )
{
// an OffTag attribute was found that is exported the same
// way as the current item
@@ -1415,55 +1420,51 @@ bool HTMLEndPosLst::ExistsOffTagItem( sal_uInt16 nWhich, sal_Int32 nStartPos,
return false;
}
void HTMLEndPosLst::FixSplittedItem( HTMLStartEndPos *pPos, sal_Int32 nNewEnd,
HTMLStartEndPositions::size_type nStartPos )
void HTMLEndPosLst::FixSplittedItem(HTMLStartEndPos* pPos, sal_Int32 nNewEnd)
{
// remove the item from the End list
std::erase(m_aEndLst[pPos->GetEnd()], pPos);
// fix the end position accordingly
pPos->SetEnd( nNewEnd );
// remove the item from the End list
HTMLStartEndPositions::iterator it = std::find(m_aEndLst.begin(), m_aEndLst.end(), pPos);
OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!");
if (it != m_aEndLst.end())
m_aEndLst.erase(it);
// from now on, it is closed as the last one at the corresponding position
HTMLStartEndPositions::size_type nEndPos {0};
while (nEndPos < m_aEndLst.size() && m_aEndLst[nEndPos]->GetEnd() <= nNewEnd)
++nEndPos;
m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pPos);
// from now on, it is closed at the corresponding position
m_aEndLst[nNewEnd].push_back(pPos);
// now, adjust the attributes that got started afterwards
for (HTMLStartEndPositions::size_type i = nStartPos + 1; i < m_aStartLst.size(); ++i)
const sal_Int32 nPos = pPos->GetStart();
for (const auto& [startPos, items] : m_aStartLst)
{
HTMLStartEndPos* pTest = m_aStartLst[i];
sal_Int32 nTestEnd = pTest->GetEnd();
if( pTest->GetStart() >= nNewEnd )
{
// the Test attribute and all the following ones start, after the
// split attribute ends
if (startPos < nPos)
continue;
if (startPos >= nNewEnd)
break;
}
else if( nTestEnd > nNewEnd )
auto it = items.begin();
if (startPos == nPos)
{
it = std::find(items.begin(), items.end(), pPos);
if (it != items.end())
++it;
}
for (; it != items.end(); ++it)
{
HTMLStartEndPos* pTest = *it;
const sal_Int32 nTestEnd = pTest->GetEnd();
if (nTestEnd <= nNewEnd)
continue;
// the Test attribute starts before the split attribute
// ends, and ends afterwards, i.e., it must be split, as well
// remove the attribute from the End list
std::erase(m_aEndLst[pTest->GetEnd()], pTest);
// set the new end
pTest->SetEnd( nNewEnd );
// remove the attribute from the End list
it = std::find(m_aEndLst.begin(), m_aEndLst.end(), pTest);
OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!");
if (it != m_aEndLst.end())
m_aEndLst.erase(it);
// it now ends as the first attribute in the respective position.
// We already know this position in the End list.
m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pTest);
// it now ends in the respective position.
m_aEndLst[nNewEnd].push_back(pTest);
// insert the 'rest' of the attribute
InsertItem( *pTest->GetItem(), nNewEnd, nTestEnd );
InsertItem( pTest->GetItem(), nNewEnd, nTestEnd );
}
}
}
@@ -1471,36 +1472,38 @@ void HTMLEndPosLst::FixSplittedItem( HTMLStartEndPos *pPos, sal_Int32 nNewEnd,
void HTMLEndPosLst::InsertItem( const SfxPoolItem& rItem, sal_Int32 nStart,
sal_Int32 nEnd )
{
HTMLStartEndPositions::size_type i;
for (i = 0; i < m_aEndLst.size(); i++)
assert(nStart < nEnd);
for (auto& [endPos, items] : m_aEndLst)
{
HTMLStartEndPos* pTest = m_aEndLst[i];
sal_Int32 nTestEnd = pTest->GetEnd();
if( nTestEnd <= nStart )
if (endPos <= nStart)
{
// the Test attribute ends, before the new one starts
continue;
}
else if( nTestEnd < nEnd )
{
if( pTest->GetStart() < nStart )
{
// the Test attribute ends, before the new one ends. Thus, the
// new attribute must be split.
InsertItem_( new HTMLStartEndPos( rItem, nStart, nTestEnd ), i );
nStart = nTestEnd;
}
}
else
if (endPos >= nEnd)
{
// the Test attribute (and all that follow) ends, before the new
// one ends
break;
}
std::sort(items.begin(), items.end(), SortEnds(m_aStartLst));
for (HTMLStartEndPos* pTest : items)
{
if( pTest->GetStart() < nStart )
{
// the Test attribute ends, before the new one ends. Thus, the
// new attribute must be split.
InsertItem_(new HTMLStartEndPos(rItem, nStart, endPos));
nStart = endPos;
}
}
}
// one attribute must still be inserted
InsertItem_( new HTMLStartEndPos( rItem, nStart, nEnd ), i );
InsertItem_(new HTMLStartEndPos(rItem, nStart, nEnd));
}
void HTMLEndPosLst::SplitItem( const SfxPoolItem& rItem, sal_Int32 nStart,
@@ -1511,59 +1514,47 @@ void HTMLEndPosLst::SplitItem( const SfxPoolItem& rItem, sal_Int32 nStart,
// first, we must search for the old items by using the start list and
// determine the new item range
for (HTMLStartEndPositions::size_type i = 0; i < m_aStartLst.size(); ++i)
for (auto& [nTestStart, items] : m_aStartLst)
{
HTMLStartEndPos* pTest = m_aStartLst[i];
sal_Int32 nTestStart = pTest->GetStart();
sal_Int32 nTestEnd = pTest->GetEnd();
if( nTestStart >= nEnd )
{
// this attribute, and all that follow, start later
break;
}
else if( nTestEnd > nStart )
for (auto it = items.begin(); it != items.end();)
{
HTMLStartEndPos* pTest = *it;
sal_Int32 nTestEnd = pTest->GetEnd();
if (nTestEnd <= nStart)
continue;
// the Test attribute ends in the range that must be deleted
const SfxPoolItem *pItem = pTest->GetItem();
const SfxPoolItem& rTestItem = pTest->GetItem();
// only the corresponding OnTag attributes have to be considered
if( pItem->Which() == nWhich &&
HTML_ON_VALUE == GetHTMLItemState( *pItem ) )
if (rTestItem.Which() == nWhich && HTML_ON_VALUE == GetHTMLItemState(rTestItem))
{
bool bDelete = true;
// if necessary, insert the second part of the split
// attribute
if (nTestEnd > nEnd)
InsertItem(pTest->GetItem(), nEnd, nTestEnd);
if( nTestStart < nStart )
{
// the start of the new attribute corresponds to the new
// end of the attribute
FixSplittedItem( pTest, nStart, i );
bDelete = false;
}
else
if (nTestStart >= nStart)
{
// the Test item only starts after the new end of the
// attribute. Therefore, it can be completely erased.
m_aStartLst.erase(m_aStartLst.begin() + i);
i--;
HTMLStartEndPositions::iterator it
= std::find(m_aEndLst.begin(), m_aEndLst.end(), pTest);
OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!");
if (it != m_aEndLst.end())
m_aEndLst.erase(it);
}
// if necessary, insert the second part of the split
// attribute
if( nTestEnd > nEnd )
{
InsertItem( *pTest->GetItem(), nEnd, nTestEnd );
}
if( bDelete )
it = items.erase(it);
std::erase(m_aEndLst[pTest->GetEnd()], pTest);
delete pTest;
continue;
}
// the start of the new attribute corresponds to the new
// end of the attribute
FixSplittedItem(pTest, nStart);
}
++it;
}
}
}
@@ -1611,8 +1602,8 @@ HTMLEndPosLst::HTMLEndPosLst(SwDoc* pD, SwDoc* pTempl, std::optional<Color> xDfl
HTMLEndPosLst::~HTMLEndPosLst()
{
OSL_ENSURE(m_aStartLst.empty(), "Start List not empty in destructor");
OSL_ENSURE(m_aEndLst.empty(), "End List not empty in destructor");
assert(IsEmpty(m_aStartLst) && "Start List not empty in destructor");
assert(IsEmpty(m_aEndLst) && "End List not empty in destructor");
}
void HTMLEndPosLst::InsertNoScript( const SfxPoolItem& rItem,
@@ -1900,53 +1891,25 @@ void HTMLEndPosLst::OutStartAttrs( SwHTMLWriter& rWrt, sal_Int32 nPos )
{
rWrt.m_bTagOn = true;
// Character border attribute must be the first which is written out
// because of border merge.
HTMLStartEndPositions::size_type nCharBoxIndex = 0;
while (nCharBoxIndex < m_aStartLst.size()
&& m_aStartLst[nCharBoxIndex]->GetItem()->Which() != RES_CHRATR_BOX)
{
++nCharBoxIndex;
}
auto it = m_aStartLst.find(nPos);
if (it == m_aStartLst.end())
return;
// the attributes of the start list are sorted in ascending order
for (HTMLStartEndPositions::size_type i = 0; i < m_aStartLst.size(); ++i)
for (HTMLStartEndPos* pPos : it->second)
{
HTMLStartEndPos *pPos = nullptr;
if (nCharBoxIndex < m_aStartLst.size())
// output the attribute
sal_uInt16 nCSS1Script = rWrt.m_nCSS1Script;
sal_uInt16 nWhich = pPos->GetItem().Which();
if( RES_TXTATR_CHARFMT == nWhich ||
RES_TXTATR_INETFMT == nWhich ||
RES_PARATR_DROP == nWhich )
{
if( i == 0 )
pPos = m_aStartLst[nCharBoxIndex];
else if( i == nCharBoxIndex )
pPos = m_aStartLst[0];
else
pPos = m_aStartLst[i];
rWrt.m_nCSS1Script = GetScriptAtPos( nPos, nCSS1Script );
}
else
pPos = m_aStartLst[i];
sal_Int32 nStart = pPos->GetStart();
if( nStart > nPos )
{
// this attribute, and all that follow, will be opened later on
break;
}
else if( nStart == nPos )
{
// output the attribute
sal_uInt16 nCSS1Script = rWrt.m_nCSS1Script;
sal_uInt16 nWhich = pPos->GetItem()->Which();
if( RES_TXTATR_CHARFMT == nWhich ||
RES_TXTATR_INETFMT == nWhich ||
RES_PARATR_DROP == nWhich )
{
rWrt.m_nCSS1Script = GetScriptAtPos( nPos, nCSS1Script );
}
HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); // was one time only - do we still need it?
Out( aHTMLAttrFnTab, *pPos->GetItem(), rWrt );
rWrt.maStartedAttributes[pPos->GetItem()->Which()]++;
rWrt.m_nCSS1Script = nCSS1Script;
}
HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); // was one time only - do we still need it?
Out( aHTMLAttrFnTab, pPos->GetItem(), rWrt );
rWrt.maStartedAttributes[pPos->GetItem().Which()]++;
rWrt.m_nCSS1Script = nCSS1Script;
}
}
@@ -1954,59 +1917,55 @@ void HTMLEndPosLst::OutEndAttrs( SwHTMLWriter& rWrt, sal_Int32 nPos )
{
rWrt.m_bTagOn = false;
// the attributes in the End list are sorted in ascending order
HTMLStartEndPositions::size_type i {0};
while (i < m_aEndLst.size())
if (nPos == SAL_MAX_INT32)
{
HTMLStartEndPos* pPos = m_aEndLst[i];
sal_Int32 nEnd = pPos->GetEnd();
for (auto& element : m_aEndLst)
OutEndAttrs(rWrt, element.second);
}
else
{
auto it = m_aEndLst.find(nPos);
if (it != m_aEndLst.end())
OutEndAttrs(rWrt, it->second);
}
}
if( SAL_MAX_INT32 == nPos || nEnd == nPos )
void HTMLEndPosLst::OutEndAttrs(SwHTMLWriter& rWrt, std::vector<HTMLStartEndPos*>& posItems)
{
std::sort(posItems.begin(), posItems.end(), SortEnds(m_aStartLst));
for (auto it = posItems.begin(); it != posItems.end(); it = posItems.erase(it))
{
HTMLStartEndPos* pPos = *it;
HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); // was one time only - do we still need it?
// Skip closing span if next character span has the same border (border merge)
bool bSkipOut = false;
if( pPos->GetItem().Which() == RES_CHRATR_BOX )
{
HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); // was one time only - do we still need it?
// Skip closing span if next character span has the same border (border merge)
bool bSkipOut = false;
if( pPos->GetItem()->Which() == RES_CHRATR_BOX )
auto& startPosItems = m_aStartLst[pPos->GetEnd()];
for (auto it2 = startPosItems.begin(); it2 != startPosItems.end(); ++it2)
{
HTMLStartEndPositions::iterator it
= std::find(m_aStartLst.begin(), m_aStartLst.end(), pPos);
OSL_ENSURE(it != m_aStartLst.end(), "Item not found in Start List!");
if (it != m_aStartLst.end())
++it;
while (it != m_aStartLst.end())
HTMLStartEndPos* pEndPos = *it2;
if( pEndPos->GetItem().Which() == RES_CHRATR_BOX &&
static_cast<const SvxBoxItem&>(pEndPos->GetItem()) ==
static_cast<const SvxBoxItem&>(pPos->GetItem()) )
{
HTMLStartEndPos *pEndPos = *it;
if( pEndPos->GetItem()->Which() == RES_CHRATR_BOX &&
*static_cast<const SvxBoxItem*>(pEndPos->GetItem()) ==
*static_cast<const SvxBoxItem*>(pPos->GetItem()) )
{
pEndPos->SetStart(pPos->GetStart());
bSkipOut = true;
break;
}
++it;
startPosItems.erase(it2);
pEndPos->SetStart(pPos->GetStart());
auto& oldStartPosItems = m_aStartLst[pEndPos->GetStart()];
oldStartPosItems.insert(oldStartPosItems.begin(), pEndPos);
bSkipOut = true;
break;
}
}
if( !bSkipOut )
{
Out( aHTMLAttrFnTab, *pPos->GetItem(), rWrt );
rWrt.maStartedAttributes[pPos->GetItem()->Which()]--;
}
RemoveItem_( i );
}
else if( nEnd > nPos )
if( !bSkipOut )
{
// this attribute, and all that follow, are closed later on
break;
Out( aHTMLAttrFnTab, pPos->GetItem(), rWrt );
rWrt.maStartedAttributes[pPos->GetItem().Which()]--;
}
else
{
// The attribute is closed before the current position. This
// is not allowed, but we can handle it anyway.
OSL_ENSURE( nEnd >= nPos,
"The attribute should've been closed a long time ago" );
i++;
}
std::erase(m_aStartLst[pPos->GetStart()], pPos);
delete pPos;
}
}