/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
#include <memory>
#include "text.hxx"
 
#include <com/sun/star/awt/CharSet.hpp>
#include <com/sun/star/awt/FontWeight.hpp>
#include <com/sun/star/awt/FontUnderline.hpp>
#include <com/sun/star/awt/XBitmap.hpp>
#include <com/sun/star/beans/XPropertyState.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/container/XIndexReplace.hpp>
#include <com/sun/star/i18n/BreakIterator.hpp>
#include <com/sun/star/i18n/ScriptDirection.hpp>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/text/FontRelief.hpp>
#include <com/sun/star/text/XTextField.hpp>
#include <com/sun/star/text/XTextRange.hpp>
#include <com/sun/star/style/LineSpacing.hpp>
#include <com/sun/star/style/LineSpacingMode.hpp>
#include <com/sun/star/style/ParagraphAdjust.hpp>
#include <com/sun/star/style/TabStop.hpp>
#include <com/sun/star/graphic/XGraphic.hpp>
 
#include <comphelper/processfactory.hxx>
#include <editeng/svxenum.hxx>
#include <editeng/frmdir.hxx>
#include <filter/msfilter/util.hxx>
#include <i18nutil/scripttypedetector.hxx>
#include <o3tl/any.hxx>
#include <sfx2/app.hxx>
#include <svl/languageoptions.hxx>
#include <oox/export/drawingml.hxx>
 
#include <vcl/settings.hxx>
#include <vcl/metric.hxx>
#include <vcl/outdev.hxx>
#include <vcl/virdev.hxx>
#include <o3tl/make_unique.hxx>
 
using namespace css;
 
static css::uno::Reference< css::i18n::XBreakIterator > xPPTBreakIter;
 
PortionObj::PortionObj(const css::uno::Reference< css::beans::XPropertySet > & rXPropSet,
                FontCollection& rFontCollection)
    : meCharColor(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , meCharHeight(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , meFontName(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , meAsianOrComplexFont(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , meCharEscapement(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , mnCharAttrHard(0)
    , mnCharAttr(0)
    , mnFont(0)
    , mnAsianOrComplexFont(0xffff)
    , mnTextSize(0)
    , mbLastPortion(true)
{
    mXPropSet = rXPropSet;
 
    ImplGetPortionValues( rFontCollection, false );
}
 
PortionObj::PortionObj(css::uno::Reference< css::text::XTextRange > & rXTextRange,
                           bool bLast, FontCollection& rFontCollection)
    : meCharColor(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , meCharHeight(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , meFontName(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , meAsianOrComplexFont(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , meCharEscapement(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , mnCharAttrHard(0)
    , mnCharColor(0)
    , mnCharAttr(0)
    , mnCharHeight(0)
    , mnFont(0)
    , mnAsianOrComplexFont(0xffff)
    , mnCharEscapement(0)
    , mbLastPortion(bLast)
{
    OUString aString( rXTextRange->getString() );
    OUString aURL;
 
    mnTextSize = aString.getLength();
    if ( bLast )
        mnTextSize++;
 
    if ( mnTextSize )
    {
        bool bRTL_endingParen = false;
        mpFieldEntry = nullptr;
        sal_uInt32 nFieldType = 0;
 
        mXPropSet.set( rXTextRange, css::uno::UNO_QUERY );
        mXPropState.set( rXTextRange, css::uno::UNO_QUERY );
 
        bool bPropSetsValid = ( mXPropSet.is() && mXPropState.is() );
        if ( bPropSetsValid )
            nFieldType = ImplGetTextField( rXTextRange, mXPropSet, aURL );
        if ( nFieldType )
        {
            mpFieldEntry.reset( new FieldEntry( nFieldType, 0, mnTextSize ) );
            if ( nFieldType >> 28 == 4 )
            {
                mpFieldEntry->aRepresentation = aString;
                mpFieldEntry->aFieldUrl = aURL;
            }
        }
        bool bSymbol = false;
 
        if ( bPropSetsValid && ImplGetPropertyValue( "CharFontCharSet", false ) )
        {
            sal_Int16 nCharset = 0;
            mAny >>= nCharset;
            if ( nCharset == css::awt::CharSet::SYMBOL )
                bSymbol = true;
        }
        if ( mpFieldEntry && ( nFieldType & 0x800000 ) )    // placeholder ?
        {
            mnTextSize = 1;
            if ( bLast )
                mnTextSize++;
            mpText.reset( new sal_uInt16[ mnTextSize ] );
            mpText[ 0 ] = 0x2a;
        }
        else
        {
            // For i39516 - a closing parenthesis that ends an RTL string is displayed backwards by PPT
            // Solution: add a Unicode Right-to-Left Mark, following the method described in i18024
            if (bLast && !aString.isEmpty()
                && aString[aString.getLength() - 1] == ')'
                && FontCollection::GetScriptDirection(aString) == css::i18n::ScriptDirection::RIGHT_TO_LEFT)
            {
                mnTextSize++;
                bRTL_endingParen = true;
            }
            mpText.reset( new sal_uInt16[ mnTextSize ] );
            sal_uInt16 nChar;
            for ( sal_Int32 i = 0; i < aString.getLength(); i++ )
            {
                nChar = static_cast<sal_uInt16>(aString[ i ]);
                if ( nChar == 0xa )
                    nChar++;
                else if ( !bSymbol )
                {
                    switch ( nChar )
                    {
                        // Currency
                        case 128:   nChar = 0x20AC; break;
                        // Punctuation and other
                        case 130:   nChar = 0x201A; break;// SINGLE LOW-9 QUOTATION MARK
                        case 131:   nChar = 0x0192; break;// LATIN SMALL LETTER F WITH HOOK
                        case 132:   nChar = 0x201E; break;// DOUBLE LOW-9 QUOTATION MARK
                                                              // LOW DOUBLE PRIME QUOTATION MARK
                        case 133:   nChar = 0x2026; break;// HORIZONTAL ELLIPSES
                        case 134:   nChar = 0x2020; break;// DAGGER
                        case 135:   nChar = 0x2021; break;// DOUBLE DAGGER
                        case 136:   nChar = 0x02C6; break;// MODIFIER LETTER CIRCUMFLEX ACCENT
                        case 137:   nChar = 0x2030; break;// PER MILLE SIGN
                        case 138:   nChar = 0x0160; break;// LATIN CAPITAL LETTER S WITH CARON
                        case 139:   nChar = 0x2039; break;// SINGLE LEFT-POINTING ANGLE QUOTATION MARK
                        case 140:   nChar = 0x0152; break;// LATIN CAPITAL LIGATURE OE
                        case 142:   nChar = 0x017D; break;// LATIN CAPITAL LETTER Z WITH CARON
                        case 145:   nChar = 0x2018; break;// LEFT SINGLE QUOTATION MARK
                                                              // MODIFIER LETTER TURNED COMMA
                        case 146:   nChar = 0x2019; break;// RIGHT SINGLE QUOTATION MARK
                                                              // MODIFIER LETTER APOSTROPHE
                        case 147:   nChar = 0x201C; break;// LEFT DOUBLE QUOTATION MARK
                                                              // REVERSED DOUBLE PRIME QUOTATION MARK
                        case 148:   nChar = 0x201D; break;// RIGHT DOUBLE QUOTATION MARK
                                                              // REVERSED DOUBLE PRIME QUOTATION MARK
                        case 149:   nChar = 0x2022; break;// BULLET
                        case 150:   nChar = 0x2013; break;// EN DASH
                        case 151:   nChar = 0x2014; break;// EM DASH
                        case 152:   nChar = 0x02DC; break;// SMALL TILDE
                        case 153:   nChar = 0x2122; break;// TRADE MARK SIGN
                        case 154:   nChar = 0x0161; break;// LATIN SMALL LETTER S WITH CARON
                        case 155:   nChar = 0x203A; break;// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
                        case 156:   nChar = 0x0153; break;// LATIN SMALL LIGATURE OE
                        case 158:   nChar = 0x017E; break;// LATIN SMALL LETTER Z WITH CARON
                        case 159:   nChar = 0x0178; break;// LATIN CAPITAL LETTER Y WITH DIAERESIS
                    }
                }
                mpText[ i ] = nChar;
            }
        }
        if ( bRTL_endingParen )
            mpText[ mnTextSize - 2 ] = 0x200F; // Unicode Right-to-Left mark
 
        if ( bLast )
            mpText[ mnTextSize - 1 ] = 0xd;
 
        if ( bPropSetsValid )
            ImplGetPortionValues( rFontCollection, true );
    }
}
 
PortionObj::PortionObj( const PortionObj& rPortionObj )
: PropStateValue( rPortionObj )
{
    ImplConstruct( rPortionObj );
}
 
PortionObj::~PortionObj()
{
    ImplClear();
}
 
void PortionObj::Write( SvStream* pStrm, bool bLast )
{
    sal_uInt32 nCount = mnTextSize;
    if ( bLast && mbLastPortion )
        nCount--;
    for ( sal_uInt32 i = 0; i < nCount; i++ )
        pStrm->WriteUInt16( mpText[ i ] );
}
 
void PortionObj::ImplGetPortionValues( FontCollection& rFontCollection, bool bGetPropStateValue )
{
 
    bool bOk = ImplGetPropertyValue( "CharFontName", bGetPropStateValue );
    meFontName = ePropState;
    if ( bOk )
    {
        FontCollectionEntry aFontDesc( *o3tl::doAccess<OUString>(mAny) );
        sal_uInt32  nCount = rFontCollection.GetCount();
        mnFont = static_cast<sal_uInt16>(rFontCollection.GetId( aFontDesc ));
        if ( mnFont == nCount )
        {
            FontCollectionEntry& rFontDesc = rFontCollection.GetLast();
            if ( ImplGetPropertyValue( "CharFontCharSet", false ) )
                mAny >>= rFontDesc.CharSet;
            if ( ImplGetPropertyValue( "CharFontFamily", false ) )
                mAny >>= rFontDesc.Family;
            if ( ImplGetPropertyValue( "CharFontPitch", false ) )
                mAny >>= rFontDesc.Pitch;
        }
    }
 
    sal_Int16 nScriptType = SvtLanguageOptions::FromSvtScriptTypeToI18N( SvtLanguageOptions::GetScriptTypeOfLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ) );
    if ( mpText && mnTextSize && xPPTBreakIter.is() )
    {
        OUString sT( reinterpret_cast<sal_Unicode *>(mpText.get()), mnTextSize );
        nScriptType = xPPTBreakIter->getScriptType( sT, 0 );
    }
    if ( nScriptType != css::i18n::ScriptType::COMPLEX )
    {
        bOk = ImplGetPropertyValue( "CharFontNameAsian", bGetPropStateValue );
        meAsianOrComplexFont = ePropState;
        if ( bOk )
        {
            FontCollectionEntry aFontDesc( *o3tl::doAccess<OUString>(mAny) );
            sal_uInt32  nCount = rFontCollection.GetCount();
            mnAsianOrComplexFont = static_cast<sal_uInt16>(rFontCollection.GetId( aFontDesc ));
            if ( mnAsianOrComplexFont == nCount )
            {
                FontCollectionEntry& rFontDesc = rFontCollection.GetLast();
                if ( ImplGetPropertyValue( "CharFontCharSetAsian", false ) )
                    mAny >>= rFontDesc.CharSet;
                if ( ImplGetPropertyValue( "CharFontFamilyAsian", false ) )
                    mAny >>= rFontDesc.Family;
                if ( ImplGetPropertyValue( "CharFontPitchAsian", false ) )
                    mAny >>= rFontDesc.Pitch;
            }
        }
    }
    else
    {
        bOk = ImplGetPropertyValue( "CharFontNameComplex", bGetPropStateValue );
        meAsianOrComplexFont = ePropState;
        if ( bOk )
        {
            FontCollectionEntry aFontDesc( *o3tl::doAccess<OUString>(mAny) );
            sal_uInt32  nCount = rFontCollection.GetCount();
            mnAsianOrComplexFont = static_cast<sal_uInt16>(rFontCollection.GetId( aFontDesc ));
            if ( mnAsianOrComplexFont == nCount )
            {
                FontCollectionEntry& rFontDesc = rFontCollection.GetLast();
                if ( ImplGetPropertyValue( "CharFontCharSetComplex", false ) )
                    mAny >>= rFontDesc.CharSet;
                if ( ImplGetPropertyValue( "CharFontFamilyComplex", false ) )
                    mAny >>= rFontDesc.Family;
                if ( ImplGetPropertyValue( "CharFontPitchComplex", false ) )
                    mAny >>= rFontDesc.Pitch;
            }
        }
    }
 
    OUString aCharHeightName, aCharWeightName, aCharLocaleName, aCharPostureName;
    switch( nScriptType )
    {
        case css::i18n::ScriptType::ASIAN :
        {
            aCharHeightName  = "CharHeightAsian";
            aCharWeightName  = "CharWeightAsian";
            aCharLocaleName  = "CharLocaleAsian";
            aCharPostureName = "CharPostureAsian";
            break;
        }
        case css::i18n::ScriptType::COMPLEX :
        {
            aCharHeightName  = "CharHeightComplex";
            aCharWeightName  = "CharWeightComplex";
            aCharLocaleName  = "CharLocaleComplex";
            aCharPostureName = "CharPostureComplex";
            break;
        }
        default:
        {
            aCharHeightName  = "CharHeight";
            aCharWeightName  = "CharWeight";
            aCharLocaleName  = "CharLocale";
            aCharPostureName = "CharPosture";
            break;
        }
    }
 
    mnCharHeight = 24;
    if ( GetPropertyValue( mAny, mXPropSet, aCharHeightName ) )
    {
        float fVal(0.0);
        if ( mAny >>= fVal )
        {
            mnCharHeight = static_cast<sal_uInt16>( fVal + 0.5 );
            meCharHeight = GetPropertyState( mXPropSet, aCharHeightName );
        }
    }
    if ( GetPropertyValue( mAny, mXPropSet, aCharWeightName ) )
    {
        float fFloat(0.0);
        if ( mAny >>= fFloat )
        {
            if ( fFloat >= css::awt::FontWeight::SEMIBOLD )
                mnCharAttr |= 1;
            if ( GetPropertyState( mXPropSet, aCharWeightName ) == css::beans::PropertyState_DIRECT_VALUE )
                mnCharAttrHard |= 1;
        }
    }
    if ( GetPropertyValue( mAny, mXPropSet, aCharLocaleName ) )
    {
        css::lang::Locale eLocale;
        if ( mAny >>= eLocale )
            meCharLocale = eLocale;
    }
    if ( GetPropertyValue( mAny, mXPropSet, aCharPostureName ) )
    {
        css::awt::FontSlant aFS;
        if ( mAny >>= aFS )
        {
            switch( aFS )
            {
                case css::awt::FontSlant_OBLIQUE :
                case css::awt::FontSlant_ITALIC :
                    mnCharAttr |= 2;
                    break;
                default:
                    break;
            }
            if ( GetPropertyState( mXPropSet, aCharPostureName ) == css::beans::PropertyState_DIRECT_VALUE )
                mnCharAttrHard |= 2;
        }
    }
 
    if ( ImplGetPropertyValue( "CharUnderline", bGetPropStateValue ) )
    {
        sal_Int16 nVal(0);
        mAny >>= nVal;
        switch ( nVal )
        {
            case css::awt::FontUnderline::SINGLE :
            case css::awt::FontUnderline::DOUBLE :
            case css::awt::FontUnderline::DOTTED :
                mnCharAttr |= 4;
        }
    }
    if ( ePropState == css::beans::PropertyState_DIRECT_VALUE )
        mnCharAttrHard |= 4;
 
    if ( ImplGetPropertyValue( "CharShadowed", bGetPropStateValue ) )
    {
        bool bBool(false);
        mAny >>= bBool;
        if ( bBool )
            mnCharAttr |= 0x10;
    }
    if ( ePropState == css::beans::PropertyState_DIRECT_VALUE )
        mnCharAttrHard |= 16;
 
    if ( ImplGetPropertyValue( "CharRelief", bGetPropStateValue ) )
    {
        sal_Int16 nVal(0);
        mAny >>= nVal;
        if ( nVal != css::text::FontRelief::NONE )
            mnCharAttr |= 512;
    }
    if ( ePropState == css::beans::PropertyState_DIRECT_VALUE )
        mnCharAttrHard |= 512;
 
    if ( ImplGetPropertyValue( "CharColor", bGetPropStateValue ) )
    {
        sal_uInt32 nSOColor = *( o3tl::doAccess<sal_uInt32>(mAny) );
        mnCharColor = nSOColor & 0xff00ff00;                            // green and hibyte
        mnCharColor |= static_cast<sal_uInt8>(nSOColor) << 16;                   // red and blue is switched
        mnCharColor |= static_cast<sal_uInt8>( nSOColor >> 16 );
    }
    meCharColor = ePropState;
 
    mnCharEscapement = 0;
    if ( ImplGetPropertyValue( "CharEscapement", bGetPropStateValue ) )
    {
        mAny >>= mnCharEscapement;
        if ( mnCharEscapement > 100 )
            mnCharEscapement = 33;
        else if ( mnCharEscapement < -100 )
            mnCharEscapement = -33;
    }
    meCharEscapement = ePropState;
}
 
void PortionObj::ImplClear()
{
    mpFieldEntry.reset();
    mpText.reset();
}
 
void PortionObj::ImplConstruct( const PortionObj& rPortionObj )
{
    meCharColor = rPortionObj.meCharColor;
    meCharHeight = rPortionObj.meCharHeight;
    meFontName = rPortionObj.meFontName;
    meAsianOrComplexFont = rPortionObj.meAsianOrComplexFont;
    meCharEscapement = rPortionObj.meCharEscapement;
    meCharLocale = rPortionObj.meCharLocale;
    mnCharAttrHard = rPortionObj.mnCharAttrHard;
 
    mbLastPortion = rPortionObj.mbLastPortion;
    mnTextSize = rPortionObj.mnTextSize;
    mnCharColor = rPortionObj.mnCharColor;
    mnCharEscapement = rPortionObj.mnCharEscapement;
    mnCharAttr = rPortionObj.mnCharAttr;
    mnCharHeight = rPortionObj.mnCharHeight;
    mnFont = rPortionObj.mnFont;
    mnAsianOrComplexFont = rPortionObj.mnAsianOrComplexFont;
 
    if ( rPortionObj.mpText )
    {
        mpText.reset( new sal_uInt16[ mnTextSize ] );
        memcpy( mpText.get(), rPortionObj.mpText.get(), mnTextSize << 1 );
    }
 
    if ( rPortionObj.mpFieldEntry )
        mpFieldEntry.reset( new FieldEntry( *( rPortionObj.mpFieldEntry ) ) );
}
 
sal_uInt32 PortionObj::ImplCalculateTextPositions( sal_uInt32 nCurrentTextPosition )
{
    if ( mpFieldEntry && ( !mpFieldEntry->nFieldStartPos ) )
    {
        mpFieldEntry->nFieldStartPos += nCurrentTextPosition;
        mpFieldEntry->nFieldEndPos += nCurrentTextPosition;
    }
    return mnTextSize;
}
 
// Return:                              0 = no TextField
//  bit28->31   text field type :
//                                      1 = Date
//                                      2 = Time
//                                      3 = SlideNumber
//                                      4 = Url
//                                      5 = DateTime
//                                      6 = header
//                                      7 = footer
//  bit24->27   text field sub type (optional)
//     23->     PPT Textfield needs a placeholder
 
sal_uInt32 PortionObj::ImplGetTextField( css::uno::Reference< css::text::XTextRange > & ,
    const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, OUString& rURL )
{
    sal_uInt32 nRetValue = 0;
    sal_Int32 nFormat;
    css::uno::Any aAny;
    if ( GetPropertyValue( aAny, rXPropSet, "TextPortionType", true ) )
    {
        auto aTextFieldType = o3tl::doAccess<OUString>(aAny);
        if ( *aTextFieldType == "TextField" )
        {
            if ( GetPropertyValue( aAny, rXPropSet, *aTextFieldType, true ) )
            {
                css::uno::Reference< css::text::XTextField > aXTextField;
                if ( aAny >>= aXTextField )
                {
                    if ( aXTextField.is() )
                    {
                        css::uno::Reference< css::beans::XPropertySet > xFieldPropSet( aXTextField, css::uno::UNO_QUERY );
                        if ( xFieldPropSet.is() )
                        {
                            OUString aFieldKind( aXTextField->getPresentation( true ) );
                            if ( aFieldKind == "Date" )
                            {
                                if ( GetPropertyValue( aAny, xFieldPropSet, "IsFix", true ) )
                                {
                                    bool bBool = false;
                                    aAny >>= bBool;
                                    if ( !bBool )  // Fixed DateFields does not exist in PPT
                                    {
                                        if ( GetPropertyValue( aAny, xFieldPropSet, "Format", true ) )
                                        {
                                            nFormat = *o3tl::doAccess<sal_Int32>(aAny);
                                            switch ( nFormat )
                                            {
                                                default:
                                                case 5 :
                                                case 4 :
                                                case 2 : nFormat = 0; break;
                                                case 8 :
                                                case 9 :
                                                case 3 : nFormat = 1; break;
                                                case 7 :
                                                case 6 : nFormat = 2; break;
                                            }
                                            nRetValue |= ( ( ( 1 << 4 ) | nFormat ) << 24 ) | 0x800000;
                                        }
                                    }
                                }
                            }
                            else if ( aFieldKind == "URL" )
                            {
                                if ( GetPropertyValue( aAny, xFieldPropSet, "URL", true ) )
                                    rURL = *o3tl::doAccess<OUString>(aAny);
                                nRetValue = 4 << 28;
                            }
                            else if ( aFieldKind == "Page" )
                            {
                                nRetValue = 3 << 28 | 0x800000;
                            }
                            else if ( aFieldKind == "Pages" )
                            {
 
                            }
                            else if ( aFieldKind == "Time" )
                            {
                                if ( GetPropertyValue( aAny, xFieldPropSet, "IsFix", true ) )
                                {
                                    bool bBool = false;
                                    aAny >>= bBool;
                                    if ( !bBool )
                                    {
                                        if ( GetPropertyValue( aAny, xFieldPropSet, "IsFix", true ) )
                                        {
                                            nFormat = *o3tl::doAccess<sal_Int32>(aAny);
                                            nRetValue |= ( ( ( 2 << 4 ) | nFormat ) << 24 ) | 0x800000;
                                        }
                                    }
                                }
                            }
                            else if ( aFieldKind == "File" )
                            {
 
                            }
                            else if ( aFieldKind == "Table" )
                            {
 
                            }
                            else if ( aFieldKind == "ExtTime" )
                            {
                                if ( GetPropertyValue( aAny, xFieldPropSet, "IsFix", true ) )
                                {
                                    bool bBool = false;
                                    aAny >>= bBool;
                                    if ( !bBool )
                                    {
                                        if ( GetPropertyValue( aAny, xFieldPropSet, "Format", true ) )
                                        {
                                            nFormat = *o3tl::doAccess<sal_Int32>(aAny);
                                            switch ( nFormat )
                                            {
                                                default:
                                                case 6 :
                                                case 7 :
                                                case 8 :
                                                case 2 : nFormat = 12; break;
                                                case 3 : nFormat = 9; break;
                                                case 5 :
                                                case 4 : nFormat = 10; break;
 
                                            }
                                            nRetValue |= ( ( ( 2 << 4 ) | nFormat ) << 24 ) | 0x800000;
                                        }
                                    }
                                }
                            }
                            else if ( aFieldKind == "ExtFile" )
                            {
 
                            }
                            else if ( aFieldKind ==  "Author" )
                            {
 
                            }
                            else if ( aFieldKind == "DateTime" )
                            {
                                nRetValue = 5 << 28 | 0x800000;
                            }
                            else if ( aFieldKind == "Header" )
                            {
                                nRetValue = 6 << 28 | 0x800000;
                            }
                            else if ( aFieldKind == "Footer" )
                            {
                                nRetValue = 7 << 28 | 0x800000;
                            }
                        }
                    }
                }
            }
        }
    }
    return nRetValue;
}
 
PortionObj& PortionObj::operator=( const PortionObj& rPortionObj )
{
    if ( this != &rPortionObj )
    {
        ImplClear();
        ImplConstruct( rPortionObj );
    }
    return *this;
}
 
ParagraphObj::ParagraphObj(const css::uno::Reference< css::beans::XPropertySet > & rXPropSet,
    PPTExBulletProvider* pProv)
    : PropStateValue()
    , SOParagraph()
    , mvPortions()
    , mnTextSize(0)
    , mbFirstParagraph(false)
    , mbLastParagraph(false)
    , mnTextAdjust(0)
    , mnLineSpacing(0)
    , mbFixedLineSpacing(false)
    , mnLineSpacingTop(0)
    , mnLineSpacingBottom(0)
    , mbForbiddenRules(false)
    , mbParagraphPunctation(false)
    , mnBiDi(0)
{
    mXPropSet = rXPropSet;
 
    bExtendedParameters = false;
 
    nDepth = 0;
    nBulletFlags = 0;
    nParaFlags = 0;
 
    ImplGetParagraphValues( pProv, false );
}
 
ParagraphObj::ParagraphObj(css::uno::Reference< css::text::XTextContent > const & rXTextContent,
    ParaFlags aParaFlags, FontCollection& rFontCollection, PPTExBulletProvider& rProv )
    : PropStateValue()
    , SOParagraph()
    , mvPortions()
    , mnTextSize(0)
    , mbIsBullet(false)
    , mbFirstParagraph( aParaFlags.bFirstParagraph )
    , mbLastParagraph( aParaFlags.bLastParagraph )
    , meBullet(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , meTextAdjust(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , meLineSpacing(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , meLineSpacingTop(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , meLineSpacingBottom(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , meForbiddenRules(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , meParagraphPunctation(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , meBiDi(css::beans::PropertyState_AMBIGUOUS_VALUE)
    , mnTextAdjust(0)
    , mnLineSpacing(0)
    , mbFixedLineSpacing(false)
    , mnLineSpacingTop(0)
    , mnLineSpacingBottom(0)
    , mbForbiddenRules(false)
    , mbParagraphPunctation(false)
    , mnBiDi(0)
{
    bExtendedParameters = false;
 
    nDepth = 0;
    nBulletFlags = 0;
    nParaFlags = 0;
 
    mXPropSet.set( rXTextContent, css::uno::UNO_QUERY );
 
    mXPropState.set( rXTextContent, css::uno::UNO_QUERY );
 
    if ( mXPropSet.is() && mXPropState.is() )
    {
        css::uno::Reference< css::container::XEnumerationAccess > aXTextPortionEA( rXTextContent, css::uno::UNO_QUERY );
        if ( aXTextPortionEA.is() )
        {
            css::uno::Reference< css::container::XEnumeration > aXTextPortionE( aXTextPortionEA->createEnumeration() );
            if ( aXTextPortionE.is() )
            {
                while ( aXTextPortionE->hasMoreElements() )
                {
                    css::uno::Reference< css::text::XTextRange > aXCursorText;
                    css::uno::Any aAny( aXTextPortionE->nextElement() );
                    if ( aAny >>= aXCursorText )
                    {
                        PortionObj* pPortionObj = new PortionObj( aXCursorText, !aXTextPortionE->hasMoreElements(), rFontCollection );
                        if ( pPortionObj->Count() )
                            mvPortions.push_back( std::unique_ptr<PortionObj>(pPortionObj) );
                        else
                            delete pPortionObj;
                    }
                }
            }
        }
        ImplGetParagraphValues( &rProv, true );
    }
}
 
ParagraphObj::ParagraphObj( const ParagraphObj& rObj )
: PropStateValue()
, SOParagraph()
, mvPortions()
{
    ImplConstruct( rObj );
}
 
ParagraphObj::~ParagraphObj()
{
    ImplClear();
}
 
void ParagraphObj::Write( SvStream* pStrm )
{
    for ( std::vector<std::unique_ptr<PortionObj> >::iterator it = mvPortions.begin(); it != mvPortions.end(); ++it )
        (*it)->Write( pStrm, mbLastParagraph );
}
 
void ParagraphObj::ImplClear()
{
    mvPortions.clear();
}
 
void ParagraphObj::CalculateGraphicBulletSize( sal_uInt16 nFontHeight )
{
    if ( ( nNumberingType == SVX_NUM_BITMAP ) && ( nBulletId != 0xffff ) )
    {
        // calculate the bullet real size for this graphic
        if ( aBuGraSize.Width() && aBuGraSize.Height() )
        {
            double fCharHeight = nFontHeight;
            double fLen = aBuGraSize.Height();
            fCharHeight = fCharHeight * 0.2540;
            double fQuo = fLen / fCharHeight;
            nBulletRealSize = static_cast<sal_Int16>( fQuo + 0.5 );
            if ( static_cast<sal_uInt16>(nBulletRealSize) > 400 )
                nBulletRealSize = 400;
        }
    }
}
 
void ParagraphObj::ImplGetNumberingLevel( PPTExBulletProvider* pBuProv, sal_Int16 nNumberingDepth, bool bIsBullet, bool bGetPropStateValue )
{
    css::uno::Any aAny;
    if ( GetPropertyValue( aAny, mXPropSet, "ParaLeftMargin" ) )
    {
        sal_Int32 nVal(0);
        if ( aAny >>= nVal )
            nTextOfs = static_cast< sal_Int16 >( nVal / ( 2540.0 / 576 ) + 0.5 ) ;
    }
    if ( GetPropertyValue( aAny, mXPropSet, "ParaFirstLineIndent" ) )
    {
        if ( aAny >>= nBulletOfs )
            nBulletOfs = static_cast< sal_Int32 >( nBulletOfs / ( 2540.0 / 576 ) + 0.5 );
    }
    if ( GetPropertyValue( aAny, mXPropSet, "NumberingIsNumber" ) )
        aAny >>= bNumberingIsNumber;
 
    css::uno::Reference< css::container::XIndexReplace > aXIndexReplace;
 
    if ( bIsBullet && ImplGetPropertyValue( "NumberingRules", bGetPropStateValue ) )
    {
        if ( ( mAny >>= aXIndexReplace ) && nNumberingDepth < aXIndexReplace->getCount() )
        {
            mAny = aXIndexReplace->getByIndex( nNumberingDepth );
            auto aPropertySequence = o3tl::doAccess<css::uno::Sequence<css::beans::PropertyValue>>(mAny);
 
            const css::beans::PropertyValue* pPropValue = aPropertySequence->getConstArray();
 
            sal_Int32 nPropertyCount = aPropertySequence->getLength();
            if ( nPropertyCount )
            {
                bExtendedParameters = true;
                nBulletRealSize = 100;
                nMappedNumType = 0;
 
                uno::Reference<graphic::XGraphic> xGraphic;
                for ( sal_Int32 i = 0; i < nPropertyCount; i++ )
                {
                    OUString aPropName( pPropValue[ i ].Name );
                    if ( aPropName == "NumberingType" )
                        nNumberingType = static_cast<SvxNumType>(*o3tl::doAccess<sal_Int16>(pPropValue[i].Value));
                    else if ( aPropName == "Adjust" )
                        nHorzAdjust = *o3tl::doAccess<sal_Int16>(pPropValue[i].Value);
                    else if ( aPropName == "BulletChar" )
                    {
                        OUString aString( *o3tl::doAccess<OUString>(pPropValue[i].Value) );
                        if ( !aString.isEmpty() )
                            cBulletId = aString[ 0 ];
                    }
                    else if ( aPropName == "BulletFont" )
                    {
                        aFontDesc = *o3tl::doAccess<css::awt::FontDescriptor>(pPropValue[i].Value);
 
                        // Our numbullet dialog has set the wrong textencoding for our "StarSymbol" font,
                        // instead of a Unicode encoding the encoding RTL_TEXTENCODING_SYMBOL was used.
                        // Because there might exist a lot of damaged documemts I added this two lines
                        // which fixes the bullet problem for the export.
                        if ( aFontDesc.Name.equalsIgnoreAsciiCase("StarSymbol") )
                            aFontDesc.CharSet = RTL_TEXTENCODING_MS_1252;
 
                    }
                    else if ( aPropName == "GraphicBitmap" )
                    {
                        auto xBitmap = pPropValue[i].Value.get<uno::Reference<awt::XBitmap>>();
                        xGraphic.set(xBitmap, uno::UNO_QUERY);
                    }
                    else if ( aPropName == "GraphicSize" )
                    {
                        if (auto aSize = o3tl::tryAccess<css::awt::Size>(pPropValue[i].Value))
                        {
                            // don't cast awt::Size to Size as on 64-bits they are not the same.
                            aBuGraSize.setWidth( aSize->Width );
                            aBuGraSize.setHeight( aSize->Height );
                        }
                    }
                    else if ( aPropName == "StartWith" )
                        nStartWith = *o3tl::doAccess<sal_Int16>(pPropValue[i].Value);
                    else if ( aPropName == "LeftMargin" )
                        nTextOfs = nTextOfs + static_cast< sal_Int16 >( *o3tl::doAccess<sal_Int32>(pPropValue[i].Value) / ( 2540.0 / 576 ) );
                    else if ( aPropName == "FirstLineOffset" )
                        nBulletOfs += static_cast<sal_Int16>( *o3tl::doAccess<sal_Int32>(pPropValue[i].Value) / ( 2540.0 / 576 ) );
                    else if ( aPropName == "BulletColor" )
                    {
                        sal_uInt32 nSOColor = *o3tl::doAccess<sal_uInt32>(pPropValue[i].Value);
                        nBulletColor = nSOColor & 0xff00ff00;                       // green and hibyte
                        nBulletColor |= static_cast<sal_uInt8>(nSOColor) << 16;              // red
                        nBulletColor |= static_cast<sal_uInt8>( nSOColor >> 16 ) | 0xfe000000; // blue
                    }
                    else if ( aPropName == "BulletRelSize" )
                    {
                        nBulletRealSize = *o3tl::doAccess<sal_Int16>(pPropValue[i].Value);
                        nParaFlags |= 0x40;
                        nBulletFlags |= 8;
                    }
                    else if ( aPropName == "Prefix" )
                        sPrefix = *o3tl::doAccess<OUString>(pPropValue[i].Value);
                    else if ( aPropName == "Suffix" )
                        sSuffix = *o3tl::doAccess<OUString>(pPropValue[i].Value);
#ifdef DBG_UTIL
                    else if ( ! (
                            ( aPropName == "SymbolTextDistance" )
                        ||  ( aPropName == "GraphicBitmap" ) ) )
                    {
                        OSL_FAIL( "Unknown Property" );
                    }
#endif
                }
 
                if (xGraphic.is())
                {
                    if ( aBuGraSize.Width() && aBuGraSize.Height() )
                    {
                        Graphic aGraphic(xGraphic);
                        nBulletId = pBuProv->GetId(xGraphic, aBuGraSize );
                        if ( nBulletId != 0xffff )
                            bExtendedBulletsUsed = true;
                    }
                    else
                    {
                        nNumberingType = SVX_NUM_NUMBER_NONE;
                    }
                }
 
                CalculateGraphicBulletSize( ( mvPortions.empty() ) ? 24 : mvPortions.front()->mnCharHeight );
 
                switch( nNumberingType )
                {
                    case SVX_NUM_NUMBER_NONE : nParaFlags |= 0xf; break;
 
                    case SVX_NUM_CHAR_SPECIAL :                           // Bullet
                    {
                        if ( IsStarSymbol(aFontDesc.Name) )
                        {
                            rtl_TextEncoding eChrSet = aFontDesc.CharSet;
                            cBulletId = msfilter::util::bestFitOpenSymbolToMSFont(cBulletId, eChrSet, aFontDesc.Name);
                            aFontDesc.CharSet = eChrSet;
                        }
 
                        if ( !aFontDesc.Name.isEmpty() )
                        {
                            nParaFlags |= 0x90; // we define the font and charset
                        }
 
                        SAL_FALLTHROUGH;
                    }
                    case SVX_NUM_CHARS_UPPER_LETTER :       // count from a-z, aa - az, ba - bz, ...
                    case SVX_NUM_CHARS_LOWER_LETTER :
                    case SVX_NUM_ROMAN_UPPER :
                    case SVX_NUM_ROMAN_LOWER :
                    case SVX_NUM_ARABIC :
                    case SVX_NUM_PAGEDESC :                 // numbering from the page template
                    case SVX_NUM_BITMAP :
                    case SVX_NUM_CHARS_UPPER_LETTER_N :     // count from a-z, aa-zz, aaa-zzz
                    case SVX_NUM_CHARS_LOWER_LETTER_N :
                    case SVX_NUM_NUMBER_UPPER_ZH:
                    case SVX_NUM_CIRCLE_NUMBER:
                    case SVX_NUM_NUMBER_UPPER_ZH_TW:
                    case SVX_NUM_NUMBER_LOWER_ZH:
                    case SVX_NUM_FULL_WIDTH_ARABIC:
                    {
                        if ( nNumberingType != SVX_NUM_CHAR_SPECIAL )
                        {
                            bExtendedBulletsUsed = true;
                            if ( nNumberingDepth & 1 )
                                cBulletId = 0x2013;         // defaulting bullet characters for ppt97
                            else if ( nNumberingDepth == 4 )
                                cBulletId = 0xbb;
                            else
                                cBulletId = 0x2022;
 
                            switch( nNumberingType )
                            {
                                case SVX_NUM_CHARS_UPPER_LETTER :
                                case SVX_NUM_CHARS_UPPER_LETTER_N :
                                {
                                    if ( sSuffix == ")" )
                                    {
                                        if ( sPrefix == "(" )
                                            nMappedNumType = 0xa0001;   // (A)
                                        else
                                            nMappedNumType = 0xb0001;   // A)
                                    }
                                    else
                                        nMappedNumType = 0x10001;       // A.
                                }
                                break;
                                case SVX_NUM_CHARS_LOWER_LETTER :
                                case SVX_NUM_CHARS_LOWER_LETTER_N :
                                {
                                    if ( sSuffix == ")" )
                                    {
                                        if ( sPrefix == "(" )
                                            nMappedNumType = 0x80001;   // (a)
                                        else
                                            nMappedNumType = 0x90001;   // a)
                                    }
                                    else
                                        nMappedNumType = 0x00001;       // a.
                                }
                                break;
                                case SVX_NUM_ROMAN_UPPER :
                                {
                                    if ( sSuffix == ")" )
                                    {
                                        if ( sPrefix == "(" )
                                            nMappedNumType = 0xe0001;   // (I)
                                        else
                                            nMappedNumType = 0xf0001;   // I)
                                    }
                                    else
                                        nMappedNumType = 0x70001;       // I.
                                }
                                break;
                                case SVX_NUM_ROMAN_LOWER :
                                {
                                    if ( sSuffix == ")" )
                                    {
                                        if ( sPrefix == "(" )
                                            nMappedNumType = 0x40001;   // (i)
                                        else
                                            nMappedNumType = 0x50001;   // i)
                                    }
                                    else
                                        nMappedNumType = 0x60001;       // i.
                                }
                                break;
                                case SVX_NUM_ARABIC :
                                {
                                    if ( sSuffix == ")" )
                                    {
                                        if ( sPrefix == "(" )
                                            nMappedNumType = 0xc0001;   // (1)
                                        else
                                            nMappedNumType = 0x20001;   // 1)
                                    }
                                    else
                                    {
                                        if ( sSuffix.isEmpty() && sPrefix.isEmpty() )
                                            nMappedNumType = 0xd0001;   // 1
                                        else
                                            nMappedNumType = 0x30001;   // 1.
                                    }
                                }
                                break;
                                case SVX_NUM_NUMBER_UPPER_ZH :
                                {
                                    if ( !sSuffix.isEmpty() )
                                        nMappedNumType = 0x110001;   // Simplified Chinese with single-byte period.
                                    else
                                        nMappedNumType = 0x100001;   // Simplified Chinese.
                                }
                                break;
                                case SVX_NUM_CIRCLE_NUMBER :
                                {
                                    nMappedNumType = 0x120001;   // Double byte circle numbers.
                                }
                                break;
                                case SVX_NUM_NUMBER_UPPER_ZH_TW :
                                {
                                    if ( !sSuffix.isEmpty() )
                                        nMappedNumType = 0x160001;   // Traditional Chinese with single-byte period.
                                    else
                                        nMappedNumType = 0x150001;   // Traditional Chinese.
                                }
                                break;
                                case SVX_NUM_NUMBER_LOWER_ZH :
                                {
                                    if ( sSuffix == OUStringLiteral1(0xff0e) )
                                        nMappedNumType = 0x260001;   // Japanese with double-byte period.
                                    else if ( !sSuffix.isEmpty() )
                                        nMappedNumType = 0x1B0001;   // Japanese/Korean with single-byte period.
                                    else
                                        nMappedNumType = 0x1A0001;   // Japanese/Korean.
                                }
                                break;
                                case SVX_NUM_FULL_WIDTH_ARABIC :
                                {
                                    if ( !sSuffix.isEmpty() )
                                        nMappedNumType = 0x1D0001;   // Double-byte Arabic numbers with double-byte period.
                                    else
                                        nMappedNumType = 0x1C0001;   // Double-byte Arabic numbers.
                                }
                                break;
                                default:
                                    break;
                            }
                        }
                        nParaFlags |= 0x2f;
                        nBulletFlags |= 6;
                        if ( mbIsBullet && bNumberingIsNumber )
                            nBulletFlags |= 1;
                        break;
                    }
                    default:
                        break;
                }
            }
        }
    }
    nBulletOfs = nTextOfs + nBulletOfs;
    if ( nBulletOfs < 0 )
        nBulletOfs = 0;
}
 
void ParagraphObj::ImplGetParagraphValues( PPTExBulletProvider* pBuProv, bool bGetPropStateValue )
{
    css::uno::Any aAny;
    if ( GetPropertyValue( aAny, mXPropSet, "NumberingLevel", true ) )
    {
        if ( bGetPropStateValue )
            meBullet = GetPropertyState( mXPropSet, "NumberingLevel" );
        nDepth = *o3tl::doAccess<sal_Int16>(aAny);
 
        if ( nDepth < 0 )
        {
            mbIsBullet = false;
            nDepth = 0;
        }
        else
        {
            if ( nDepth > 4 )
                nDepth = 4;
            mbIsBullet = true;
        }
    }
    else
    {
        nDepth = 0;
        mbIsBullet = false;
    }
    ImplGetNumberingLevel( pBuProv, nDepth, mbIsBullet, bGetPropStateValue );
 
    if ( ImplGetPropertyValue( "ParaTabStops", bGetPropStateValue ) )
        maTabStop = *o3tl::doAccess<css::uno::Sequence<css::style::TabStop>>(mAny);
    sal_Int16 eTextAdjust = sal_Int16(css::style::ParagraphAdjust_LEFT);
    if ( GetPropertyValue( aAny, mXPropSet, "ParaAdjust", bGetPropStateValue ) )
        aAny >>= eTextAdjust;
    switch ( static_cast<css::style::ParagraphAdjust>(eTextAdjust) )
    {
        case css::style::ParagraphAdjust_CENTER :
            mnTextAdjust = 1;
        break;
        case css::style::ParagraphAdjust_RIGHT :
            mnTextAdjust = 2;
        break;
        case css::style::ParagraphAdjust_BLOCK :
            mnTextAdjust = 3;
        break;
        default :
        case css::style::ParagraphAdjust_LEFT :
            mnTextAdjust = 0;
        break;
    }
    meTextAdjust = ePropState;
 
    if ( ImplGetPropertyValue( "ParaLineSpacing", bGetPropStateValue ) )
    {
        css::style::LineSpacing aLineSpacing
            = *o3tl::doAccess<css::style::LineSpacing>(mAny);
        switch ( aLineSpacing.Mode )
        {
            case css::style::LineSpacingMode::FIX :
                mnLineSpacing = static_cast<sal_Int16>(-( aLineSpacing.Height ) );
                mbFixedLineSpacing = true;
                break;
            case css::style::LineSpacingMode::MINIMUM :
            case css::style::LineSpacingMode::LEADING :
                mnLineSpacing = static_cast<sal_Int16>(-( aLineSpacing.Height ) );
                mbFixedLineSpacing = false;
           break;
 
            case css::style::LineSpacingMode::PROP :
            default:
                mnLineSpacing = static_cast<sal_Int16>( aLineSpacing.Height );
            break;
        }
    }
    meLineSpacing = ePropState;
 
    if ( ImplGetPropertyValue( "ParaBottomMargin", bGetPropStateValue ) )
    {
        double fSpacing = *o3tl::doAccess<sal_uInt32>(mAny) + ( 2540.0 / 576.0 ) - 1;
        mnLineSpacingBottom = static_cast<sal_Int16>(-( fSpacing * 576.0 / 2540.0 ) );
    }
    meLineSpacingBottom = ePropState;
 
    if ( ImplGetPropertyValue( "ParaTopMargin", bGetPropStateValue ) )
    {
        double fSpacing = *o3tl::doAccess<sal_uInt32>(mAny) + ( 2540.0 / 576.0 ) - 1;
        mnLineSpacingTop = static_cast<sal_Int16>(-( fSpacing * 576.0 / 2540.0 ) );
    }
    meLineSpacingTop = ePropState;
 
    if ( ImplGetPropertyValue( "ParaIsForbiddenRules", bGetPropStateValue ) )
        mAny >>= mbForbiddenRules;
    meForbiddenRules = ePropState;
 
    if ( ImplGetPropertyValue( "ParaIsHangingPunctuation", bGetPropStateValue ) )
        mAny >>= mbParagraphPunctation;
    meParagraphPunctation = ePropState;
 
    mnBiDi = 0;
    if ( ImplGetPropertyValue( "WritingMode", bGetPropStateValue ) )
    {
        sal_Int16 nWritingMode = 0;
        mAny >>= nWritingMode;
 
        SvxFrameDirection eWritingMode = static_cast<SvxFrameDirection>(nWritingMode);
        if ( ( eWritingMode == SvxFrameDirection::Horizontal_RL_TB )
            || ( eWritingMode == SvxFrameDirection::Vertical_RL_TB ) )
        {
            mnBiDi = 1;
        }
    }
    meBiDi = ePropState;
}
 
void ParagraphObj::ImplConstruct( const ParagraphObj& rParagraphObj )
{
    mbIsBullet = rParagraphObj.mbIsBullet;
    meBullet = rParagraphObj.meBullet;
    meTextAdjust = rParagraphObj.meTextAdjust;
    meLineSpacing = rParagraphObj.meLineSpacing;
    meLineSpacingTop = rParagraphObj.meLineSpacingTop;
    meLineSpacingBottom = rParagraphObj.meLineSpacingBottom;
    meForbiddenRules = rParagraphObj.meForbiddenRules;
    meParagraphPunctation = rParagraphObj.meParagraphPunctation;
    meBiDi =rParagraphObj.meBiDi;
    mbFixedLineSpacing = rParagraphObj.mbFixedLineSpacing;
    mnTextSize = rParagraphObj.mnTextSize;
    mnTextAdjust = rParagraphObj.mnTextAdjust;
    mnLineSpacing = rParagraphObj.mnLineSpacing;
    mnLineSpacingTop = rParagraphObj.mnLineSpacingTop;
    mnLineSpacingBottom = rParagraphObj.mnLineSpacingBottom;
    mbFirstParagraph = rParagraphObj.mbFirstParagraph;
    mbLastParagraph = rParagraphObj.mbLastParagraph;
    mbParagraphPunctation = rParagraphObj.mbParagraphPunctation;
    mbForbiddenRules = rParagraphObj.mbForbiddenRules;
    mnBiDi = rParagraphObj.mnBiDi;
 
    for ( std::vector<std::unique_ptr<PortionObj> >::const_iterator it = rParagraphObj.begin(); it != rParagraphObj.end(); ++it )
        mvPortions.push_back( o3tl::make_unique<PortionObj>( **it ) );
 
    maTabStop = rParagraphObj.maTabStop;
    bExtendedParameters = rParagraphObj.bExtendedParameters;
    nParaFlags = rParagraphObj.nParaFlags;
    nBulletFlags = rParagraphObj.nBulletFlags;
    sPrefix = rParagraphObj.sPrefix;
    sSuffix = rParagraphObj.sSuffix;
    sGraphicUrl = rParagraphObj.sGraphicUrl;            // String to a graphic
    aBuGraSize = rParagraphObj.aBuGraSize;
    nNumberingType = rParagraphObj.nNumberingType;      // this is actually a SvxEnum
    nHorzAdjust = rParagraphObj.nHorzAdjust;
    nBulletColor = rParagraphObj.nBulletColor;
    nBulletOfs = rParagraphObj.nBulletOfs;
    nStartWith = rParagraphObj.nStartWith;              // start of numbering
    nTextOfs = rParagraphObj.nTextOfs;
    nBulletRealSize = rParagraphObj.nBulletRealSize;    // scale in percent
    nDepth = rParagraphObj.nDepth;                      // actual depth
    cBulletId = rParagraphObj.cBulletId;                // if Numbering Type == CharSpecial
    aFontDesc = rParagraphObj.aFontDesc;
 
    bExtendedBulletsUsed = rParagraphObj.bExtendedBulletsUsed;
    nBulletId = rParagraphObj.nBulletId;
}
 
sal_uInt32 ParagraphObj::ImplCalculateTextPositions( sal_uInt32 nCurrentTextPosition )
{
    mnTextSize = 0;
    for ( std::vector<std::unique_ptr<PortionObj> >::iterator it = mvPortions.begin(); it != mvPortions.end(); ++it )
        mnTextSize += (*it)->ImplCalculateTextPositions( nCurrentTextPosition + mnTextSize );
    return mnTextSize;
}
 
ParagraphObj& ParagraphObj::operator=( const ParagraphObj& rParagraphObj )
{
    if ( this != &rParagraphObj )
    {
        ImplClear();
        ImplConstruct( rParagraphObj );
    }
    return *this;
}
 
struct ImplTextObj
{
    sal_uInt32      mnTextSize;
    int             mnInstance;
    std::vector<ParagraphObj*> maList;
    bool        mbHasExtendedBullets;
 
    explicit ImplTextObj( int nInstance );
    ~ImplTextObj();
};
 
ImplTextObj::ImplTextObj( int nInstance )
  : maList()
{
    mnTextSize = 0;
    mnInstance = nInstance;
    mbHasExtendedBullets = false;
}
 
ImplTextObj::~ImplTextObj()
{
    for ( std::vector<ParagraphObj*>::const_iterator it = maList.begin(); it != maList.end(); ++it )
        delete *it;
}
 
TextObj::TextObj( css::uno::Reference< css::text::XSimpleText > const & rXTextRef,
            int nInstance, FontCollection& rFontCollection, PPTExBulletProvider& rProv ):
    mpImplTextObj(new ImplTextObj(nInstance))
{
    css::uno::Reference< css::container::XEnumerationAccess > aXTextParagraphEA( rXTextRef, css::uno::UNO_QUERY );
 
    if ( aXTextParagraphEA.is()  )
    {
        css::uno::Reference< css::container::XEnumeration > aXTextParagraphE( aXTextParagraphEA->createEnumeration() );
        if ( aXTextParagraphE.is() )
        {
            ParaFlags aParaFlags;
            while ( aXTextParagraphE->hasMoreElements() )
            {
                css::uno::Reference< css::text::XTextContent > aXParagraph;
                css::uno::Any aAny( aXTextParagraphE->nextElement() );
                if ( aAny >>= aXParagraph )
                {
                    if ( !aXTextParagraphE->hasMoreElements() )
                        aParaFlags.bLastParagraph = true;
                    ParagraphObj* pPara = new ParagraphObj( aXParagraph, aParaFlags, rFontCollection, rProv );
                    mpImplTextObj->mbHasExtendedBullets |= pPara->bExtendedBulletsUsed;
                    mpImplTextObj->maList.push_back( pPara );
                    aParaFlags.bFirstParagraph = false;
                }
            }
        }
    }
    ImplCalculateTextPositions();
}
 
void TextObj::ImplCalculateTextPositions()
{
    mpImplTextObj->mnTextSize = 0;
    for ( sal_uInt32 i = 0; i < ParagraphCount(); ++i )
        mpImplTextObj->mnTextSize += GetParagraph(i)->ImplCalculateTextPositions( mpImplTextObj->mnTextSize );
}
 
ParagraphObj* TextObj::GetParagraph(int idx)
{
    return mpImplTextObj->maList[idx];
}
 
sal_uInt32 TextObj::ParagraphCount() const
{
    return mpImplTextObj->maList.size();
}
 
sal_uInt32 TextObj::Count() const
{
    return mpImplTextObj->mnTextSize;
}
 
int TextObj::GetInstance() const
{
    return mpImplTextObj->mnInstance;
}
 
bool TextObj::HasExtendedBullets()
{
    return mpImplTextObj->mbHasExtendedBullets;
}
 
FontCollectionEntry::~FontCollectionEntry()
{
}
 
void FontCollectionEntry::ImplInit( const OUString& rName )
{
    OUString aSubstName( GetSubsFontName( rName, SubsFontFlags::ONLYONE | SubsFontFlags::MS ) );
    if ( !aSubstName.isEmpty() )
    {
        Name = aSubstName;
    }
    else
    {
        Name = rName;
    }
}
 
FontCollection::~FontCollection()
{
    pVDev.disposeAndClear();
    xPPTBreakIter = nullptr;
}
 
FontCollection::FontCollection() :
    pVDev ( nullptr )
{
    xPPTBreakIter = css::i18n::BreakIterator::create( ::comphelper::getProcessComponentContext() );
}
 
short FontCollection::GetScriptDirection( const OUString& rString )
{
    short nRet = ScriptTypeDetector::getScriptDirection( rString, 0, css::i18n::ScriptDirection::NEUTRAL );
    return nRet;
}
 
sal_uInt32 FontCollection::GetId( FontCollectionEntry& rEntry )
{
    if( !rEntry.Name.isEmpty() )
    {
        const sal_uInt32 nFonts = maFonts.size();
 
        for( sal_uInt32 i = 0; i < nFonts; i++ )
        {
            const FontCollectionEntry* pEntry = GetById( i );
            if( pEntry->Name == rEntry.Name )
                return i;
        }
        vcl::Font aFont;
        aFont.SetCharSet( rEntry.CharSet );
        aFont.SetFamilyName( rEntry.Original );
        aFont.SetFontHeight( 100 );
 
        if ( !pVDev )
            pVDev = VclPtr<VirtualDevice>::Create();
 
        pVDev->SetFont( aFont );
        FontMetric aMetric( pVDev->GetFontMetric() );
 
        sal_uInt16 nTxtHeight = static_cast<sal_uInt16>(aMetric.GetAscent()) + static_cast<sal_uInt16>(aMetric.GetDescent());
 
        if ( nTxtHeight )
        {
            double fScaling = static_cast<double>(nTxtHeight) / 120.0;
            if ( ( fScaling > 0.50 ) && ( fScaling < 1.5 ) )
                rEntry.Scaling = fScaling;
        }
 
        maFonts.push_back(rEntry);
        return nFonts;
    }
    return 0;
}
 
const FontCollectionEntry* FontCollection::GetById( sal_uInt32 nId )
{
    return nId < maFonts.size() ? &maFonts[nId] : nullptr;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V547 Expression 'nVal != css::text::FontRelief::NONE' is always false.

V547 Expression '!bBool' is always true.

V547 Expression '!bBool' is always true.

V785 Constant expression in switch statement.

V547 Expression 'bBool' is always false.

V547 Expression '!bBool' is always true.