tdf#173712 oox: continue a PPTX numbered list that repeats startAt
PowerPoint writes the same startAt on every paragraph of a list, and since tdf#168406 each startAt restarted it: 2. 2. 2. instead of 2. 3. 4. Restart only when a list begins at that level or its numbering type or startAt changes, a missing startAt being startAt="1". Paragraphs without a bullet of their own, also without a:pPr, take it from a:lstStyle. Don't set the restart flag in a:lstStyle itself: every paragraph inherits it. An unnumbered paragraph ends the lists at its level and deeper. Change-Id: I26720a6ba54a8050cfb6e0ba892096039c391c36 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211632 Reviewed-by: David Gilbert <freedesktop@treblig.org> Tested-by: Jenkins
This commit is contained in:
committed by
David Gilbert
parent
1440fb4cb8
commit
8f242675c1
@@ -20,9 +20,8 @@
|
||||
#ifndef INCLUDED_OOX_DRAWINGML_TEXTBODYCONTEXT_HXX
|
||||
#define INCLUDED_OOX_DRAWINGML_TEXTBODYCONTEXT_HXX
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <drawingml/textbody.hxx>
|
||||
#include <drawingml/textparagraphpropertiescontext.hxx>
|
||||
#include <drawingml/textrun.hxx>
|
||||
#include <oox/core/contexthandler2.hxx>
|
||||
|
||||
@@ -39,7 +38,7 @@ public:
|
||||
private:
|
||||
TextBody& mrTextBody;
|
||||
ShapePtr mpShapePtr;
|
||||
uint16_t mListNumberingMask;
|
||||
ListNumberingState maListNumberingState;
|
||||
};
|
||||
|
||||
// CT_RegularTextRun
|
||||
|
||||
@@ -82,6 +82,7 @@ public:
|
||||
sal_Int16 getLevel( ) const { return mnLevel; }
|
||||
PropertyMap& getTextParagraphPropertyMap() { return maTextParagraphPropertyMap; }
|
||||
BulletList& getBulletList() { return maBulletList; }
|
||||
const BulletList& getBulletList() const { return maBulletList; }
|
||||
TextCharacterProperties& getTextCharacterProperties() { return maTextCharacterProperties; }
|
||||
const TextCharacterProperties& getTextCharacterProperties() const { return maTextCharacterProperties; }
|
||||
|
||||
|
||||
@@ -20,41 +20,60 @@
|
||||
#ifndef INCLUDED_OOX_DRAWINGML_TEXTPARAGRAPHPROPERTIESCONTEXT_HXX
|
||||
#define INCLUDED_OOX_DRAWINGML_TEXTPARAGRAPHPROPERTIESCONTEXT_HXX
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <com/sun/star/style/TabStop.hpp>
|
||||
#include <drawingml/textliststyle.hxx>
|
||||
#include <drawingml/textparagraphproperties.hxx>
|
||||
#include <oox/core/contexthandler2.hxx>
|
||||
|
||||
namespace oox::drawingml {
|
||||
|
||||
// Numbered list state of a text body, shared by its paragraphs
|
||||
struct ListNumberingState
|
||||
{
|
||||
explicit ListNumberingState(const TextListStyle& rListStyle)
|
||||
: mrListStyle(rListStyle)
|
||||
{
|
||||
}
|
||||
|
||||
// Numbering type, prefix, suffix and start value of a numbered paragraph
|
||||
struct Num
|
||||
{
|
||||
css::uno::Any maType, maPrefix, maSuffix, maStartAt;
|
||||
bool operator==(const Num&) const = default;
|
||||
};
|
||||
|
||||
// Numbering of the last numbered paragraph at each level, empty if we're not in a list
|
||||
// there (n=0 is what pptx calls level 1)
|
||||
std::array<Num, NUM_TEXT_LIST_STYLE_ENTRIES> maLastNum;
|
||||
// a:lstStyle of the text body: a paragraph without a bullet of its own takes it from there
|
||||
const TextListStyle& mrListStyle;
|
||||
|
||||
// A paragraph at nLevel with rBullet as its own bullet; returns true if its numbering restarts
|
||||
bool markParagraph(int nLevel, const BulletList& rBullet);
|
||||
};
|
||||
|
||||
class TextParagraphPropertiesContext final : public ::oox::core::ContextHandler2
|
||||
{
|
||||
public:
|
||||
TextParagraphPropertiesContext( ::oox::core::ContextHandler2Helper const & rParent,
|
||||
const ::oox::AttributeList& rAttributes,
|
||||
TextParagraphProperties& rTextParagraphProperties,
|
||||
uint16_t* pListNumberingMask = nullptr);
|
||||
ListNumberingState* pListNumberingState = nullptr);
|
||||
virtual ~TextParagraphPropertiesContext() override;
|
||||
|
||||
virtual ::oox::core::ContextHandlerRef onCreateContext( ::sal_Int32 Element, const ::oox::AttributeList& rAttribs ) override;
|
||||
|
||||
private:
|
||||
// Returns True iff we're tracking numbered lists, and we were not already in a numbered list at this level
|
||||
bool markListNumbered();
|
||||
void markListUnnumbered();
|
||||
|
||||
TextParagraphProperties& mrTextParagraphProperties;
|
||||
BulletList& mrBulletList;
|
||||
std::vector< css::style::TabStop > maTabList;
|
||||
std::shared_ptr< BlipFillProperties > mxBlipProps;
|
||||
|
||||
// A pointer to an integer mask where we track the list status
|
||||
// If bit 'n' is 1 then we're in a numbered list at that level
|
||||
// (n=0 is what pptx calls level 1)
|
||||
uint16_t* mpListNumberingMask;
|
||||
bool mbHaveNum;
|
||||
// Where we track the list status, if we do
|
||||
ListNumberingState* mpListNumberingState;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -49,25 +49,35 @@ namespace {
|
||||
class TextParagraphContext : public ContextHandler2
|
||||
{
|
||||
public:
|
||||
TextParagraphContext( ContextHandler2Helper const & rParent, TextParagraph& rPara, uint16_t& rListNumberingMask );
|
||||
TextParagraphContext( ContextHandler2Helper const & rParent, TextParagraph& rPara, ListNumberingState& rListNumberingState );
|
||||
|
||||
virtual ContextHandlerRef onCreateContext( sal_Int32 aElementToken, const AttributeList& rAttribs ) override;
|
||||
virtual void onEndElement() override;
|
||||
|
||||
protected:
|
||||
TextParagraph& mrParagraph;
|
||||
uint16_t& mrListNumberingMask;
|
||||
ListNumberingState& mrListNumberingState;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
TextParagraphContext::TextParagraphContext( ContextHandler2Helper const & rParent, TextParagraph& rPara, uint16_t& rListNumberingMask )
|
||||
TextParagraphContext::TextParagraphContext( ContextHandler2Helper const & rParent, TextParagraph& rPara, ListNumberingState& rListNumberingState )
|
||||
: ContextHandler2( rParent )
|
||||
, mrParagraph( rPara )
|
||||
, mrListNumberingMask( rListNumberingMask )
|
||||
, mrListNumberingState( rListNumberingState )
|
||||
{
|
||||
mbEnableTrimSpace = false;
|
||||
}
|
||||
|
||||
void TextParagraphContext::onEndElement()
|
||||
{
|
||||
// A paragraph without a:pPr is at level 0, with the bullet of the list style
|
||||
TextParagraphProperties& rProps = mrParagraph.getProperties();
|
||||
if (isCurrentElement(A_TOKEN(p)) && !mrParagraph.hasProperties()
|
||||
&& mrListNumberingState.markParagraph(0, rProps.getBulletList()))
|
||||
rProps.setRestartNumbering(true);
|
||||
}
|
||||
|
||||
ContextHandlerRef TextParagraphContext::onCreateContext( sal_Int32 aElementToken, const AttributeList& rAttribs )
|
||||
{
|
||||
// EG_TextRun
|
||||
@@ -96,7 +106,7 @@ ContextHandlerRef TextParagraphContext::onCreateContext( sal_Int32 aElementToken
|
||||
case A_TOKEN( pPr ):
|
||||
case W_TOKEN( pPr ):
|
||||
mrParagraph.setHasProperties();
|
||||
return new TextParagraphPropertiesContext( *this, rAttribs, mrParagraph.getProperties(), &mrListNumberingMask );
|
||||
return new TextParagraphPropertiesContext( *this, rAttribs, mrParagraph.getProperties(), &mrListNumberingState );
|
||||
case A_TOKEN( endParaRPr ):
|
||||
return new TextCharacterPropertiesContext( *this, rAttribs, mrParagraph.getEndProperties() );
|
||||
case W_TOKEN( sdt ):
|
||||
@@ -180,7 +190,7 @@ ContextHandlerRef RegularTextRunContext::onCreateContext( sal_Int32 aElementToke
|
||||
TextBodyContext::TextBodyContext( ContextHandler2Helper const & rParent, TextBody& rTextBody )
|
||||
: ContextHandler2( rParent )
|
||||
, mrTextBody( rTextBody )
|
||||
, mListNumberingMask(0)
|
||||
, maListNumberingState( rTextBody.getTextListStyle() )
|
||||
{
|
||||
}
|
||||
|
||||
@@ -205,7 +215,7 @@ ContextHandlerRef TextBodyContext::onCreateContext( sal_Int32 aElementToken, con
|
||||
return new TextListStyleContext( *this, mrTextBody.getTextListStyle() );
|
||||
case A_TOKEN( p ): // CT_TextParagraph
|
||||
case W_TOKEN( p ):
|
||||
return new TextParagraphContext( *this, mrTextBody.addParagraph(), mListNumberingMask );
|
||||
return new TextParagraphContext( *this, mrTextBody.addParagraph(), maListNumberingState );
|
||||
case W_TOKEN( sdt ):
|
||||
case W_TOKEN( sdtContent ):
|
||||
return this;
|
||||
|
||||
@@ -19,7 +19,10 @@
|
||||
|
||||
#include <drawingml/textparagraphpropertiescontext.hxx>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <com/sun/star/text/WritingMode2.hpp>
|
||||
#include <com/sun/star/style/NumberingType.hpp>
|
||||
#include <com/sun/star/style/ParagraphAdjust.hpp>
|
||||
#include <com/sun/star/xml/sax/SAXException.hpp>
|
||||
#include <com/sun/star/graphic/XGraphic.hpp>
|
||||
@@ -77,12 +80,11 @@ double lclGetGraphicAspectRatio( const Reference< XGraphic >& rxGraphic )
|
||||
TextParagraphPropertiesContext::TextParagraphPropertiesContext( ContextHandler2Helper const & rParent,
|
||||
const AttributeList& rAttribs,
|
||||
TextParagraphProperties& rTextParagraphProperties,
|
||||
uint16_t* pListNumberingMask)
|
||||
ListNumberingState* pListNumberingState)
|
||||
: ContextHandler2( rParent )
|
||||
, mrTextParagraphProperties( rTextParagraphProperties )
|
||||
, mrBulletList( rTextParagraphProperties.getBulletList() )
|
||||
, mpListNumberingMask( pListNumberingMask )
|
||||
, mbHaveNum( false )
|
||||
, mpListNumberingState( pListNumberingState )
|
||||
{
|
||||
OUString sValue;
|
||||
|
||||
@@ -192,8 +194,12 @@ TextParagraphPropertiesContext::~TextParagraphPropertiesContext()
|
||||
mrBulletList.setBulletAspectRatio( lclGetGraphicAspectRatio(mxBlipProps->mxFillGraphic) );
|
||||
}
|
||||
|
||||
if( !mbHaveNum )
|
||||
markListUnnumbered();
|
||||
// The list is tracked only for a:pPr of a paragraph. Not for a:lvl1pPr etc. of a list
|
||||
// style: a restart there would be inherited by every paragraph using the style
|
||||
if (mpListNumberingState
|
||||
&& mpListNumberingState->markParagraph(mrTextParagraphProperties.getLevel(), mrBulletList))
|
||||
mrTextParagraphProperties.setRestartNumbering(true);
|
||||
|
||||
if( mrBulletList.is() )
|
||||
rPropertyMap.setProperty( PROP_IsNumbering, true);
|
||||
sal_Int16 nLevel = mrTextParagraphProperties.getLevel();
|
||||
@@ -204,50 +210,30 @@ TextParagraphPropertiesContext::~TextParagraphPropertiesContext()
|
||||
rPropertyMap.setProperty( PROP_ParaAdjust, *mrTextParagraphProperties.getParaAdjust());
|
||||
}
|
||||
|
||||
bool TextParagraphPropertiesContext::markListNumbered()
|
||||
bool ListNumberingState::markParagraph(int nLevel, const BulletList& rBullet)
|
||||
{
|
||||
sal_Int16 nLevel = mrTextParagraphProperties.getLevel();
|
||||
|
||||
// We only track list state in some situations
|
||||
if (mpListNumberingMask == nullptr)
|
||||
const BulletList& rNumBullet
|
||||
= rBullet.is() ? rBullet : mrListStyle.getListStyle()[nLevel].getBulletList();
|
||||
sal_Int16 nType = NumberingType::NUMBER_NONE;
|
||||
rNumBullet.mnNumberingType >>= nType;
|
||||
if (nType == NumberingType::NUMBER_NONE || nType == NumberingType::CHAR_SPECIAL
|
||||
|| nType == NumberingType::BITMAP)
|
||||
{
|
||||
// Lists at our level and deeper end
|
||||
std::fill(maLastNum.begin() + nLevel, maLastNum.end(), Num());
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t nOldMask = *mpListNumberingMask;
|
||||
// The bit that represents this level (..0001000)
|
||||
uint16_t nOurBit = static_cast<uint16_t>(1) << nLevel;
|
||||
// Lists at deeper levels end
|
||||
std::fill(maLastNum.begin() + nLevel + 1, maLastNum.end(), Num());
|
||||
|
||||
uint16_t nTmp = nOldMask;
|
||||
// Clears all bits at our level and lower (..0000xxx)
|
||||
nTmp &= nOurBit - 1;
|
||||
// and put our bit in (..0001xxx)
|
||||
nTmp |= nOurBit;
|
||||
|
||||
*mpListNumberingMask = nTmp;
|
||||
|
||||
return (nOldMask & nOurBit) == 0;
|
||||
}
|
||||
|
||||
void TextParagraphPropertiesContext::markListUnnumbered()
|
||||
{
|
||||
sal_Int16 nLevel = mrTextParagraphProperties.getLevel();
|
||||
|
||||
// We only track list state in some situations
|
||||
if (mpListNumberingMask == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t nOldMask = *mpListNumberingMask;
|
||||
// The bit that represents this level (..0001000)
|
||||
uint16_t nOurBit = static_cast<uint16_t>(1) << nLevel;
|
||||
|
||||
uint16_t nTmp = nOldMask;
|
||||
// Clears all bits at our level and lower (..0000xxx)
|
||||
nTmp &= nOurBit - 1;
|
||||
|
||||
*mpListNumberingMask = nTmp;
|
||||
// A list continues while its numbering stays the same at this level,
|
||||
// a missing startAt being the same as startAt="1"
|
||||
Num aNum{ rNumBullet.mnNumberingType, rNumBullet.msNumberingPrefix,
|
||||
rNumBullet.msNumberingSuffix, rNumBullet.mnStartAt };
|
||||
bool bRestart = maLastNum[nLevel] != aNum;
|
||||
maLastNum[nLevel] = aNum;
|
||||
return bRestart;
|
||||
}
|
||||
|
||||
ContextHandlerRef TextParagraphPropertiesContext::onCreateContext( sal_Int32 aElementToken, const AttributeList& rAttribs )
|
||||
@@ -292,16 +278,9 @@ ContextHandlerRef TextParagraphPropertiesContext::onCreateContext( sal_Int32 aEl
|
||||
break;
|
||||
case A_TOKEN( buAutoNum ): // CT_TextAutonumberBullet
|
||||
{
|
||||
bool bStartingNumList = markListNumbered();
|
||||
mbHaveNum = true;
|
||||
|
||||
try {
|
||||
sal_Int32 nType = rAttribs.getToken( XML_type, 0 );
|
||||
sal_Int32 nStartAt = rAttribs.getInteger( XML_startAt, -1 );
|
||||
if( nStartAt >= 0 || bStartingNumList )
|
||||
{
|
||||
mrTextParagraphProperties.setRestartNumbering(true);
|
||||
}
|
||||
|
||||
if( nStartAt > 32767 )
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user