Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/xmloff/source/style/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 82 kB image not shown  

Quelle  xmlnumfe.cxx   Sprache: C

 
/* -*- 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 <comphelper/sequence.hxx>
#include <comphelper/string.hxx>
#include <svl/numformat.hxx>
#include <svl/zforlist.hxx>
#include <svl/zformat.hxx>
#include <svl/numuno.hxx>
#include <i18nlangtag/mslangid.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <tools/debug.hxx>
#include <rtl/math.hxx>
#include <unotools/calendarwrapper.hxx>
#include <unotools/charclass.hxx>
#include <com/sun/star/lang/Locale.hpp>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>
#include <tools/color.hxx>
#include <sax/tools/converter.hxx>

#include <com/sun/star/i18n/NativeNumberXmlAttributes2.hpp>

#include <utility>
#include <xmloff/xmlnumfe.hxx>
#include <xmloff/xmlnamespace.hxx>
#include <xmloff/xmlnumfi.hxx>

#include <svl/nfsymbol.hxx>
#include <xmloff/xmltoken.hxx>
#include <xmloff/xmlexp.hxx>
#include <o3tl/string_view.hxx>

#include <float.h>
#include <set>
#include <string_view>
#include <vector>

using namespace ::com::sun::star;
using namespace ::xmloff::token;
using namespace ::svt;

typedef std::set< sal_uInt32 >  SvXMLuInt32Set;

namespace {

struct SvXMLEmbeddedTextEntry
{
    sal_uInt16      nSourcePos;     // position in NumberFormat (to skip later)
    sal_Int32       nFormatPos;     // resulting position in embedded-text element
    OUString   aText;
    bool            isBlankWidth;   // "_x"

    SvXMLEmbeddedTextEntry( sal_uInt16 nSP, sal_Int32 nFP, OUString aT, bool bBW = false ) :
        nSourcePos(nSP), nFormatPos(nFP), aText(std::move(aT)), isBlankWidth( bBW ) {}
};

}

class SvXMLEmbeddedTextEntryArr
{
    typedef std::vector<SvXMLEmbeddedTextEntry> DataType;
    DataType maData;

public:

    void push_back( SvXMLEmbeddedTextEntry const& r )
    {
        maData.push_back(r);
    }

    const SvXMLEmbeddedTextEntry& operator[] ( size_t i ) const
    {
        return maData[i];
    }

    size_t size() const
    {
        return maData.size();
    }
};

class SvXMLNumUsedList_Impl
{
    SvXMLuInt32Set              aUsed;
    SvXMLuInt32Set              aWasUsed;
    SvXMLuInt32Set::iterator    aCurrentUsedPos;
    sal_uInt32                  nUsedCount;
    sal_uInt32                  nWasUsedCount;

public:
            SvXMLNumUsedList_Impl();

    void    SetUsed( sal_uInt32 nKey );
    bool    IsUsed( sal_uInt32 nKey ) const;
    bool    IsWasUsed( sal_uInt32 nKey ) const;
    void    Export();

    bool    GetFirstUsed(sal_uInt32& nKey);
    bool    GetNextUsed(sal_uInt32& nKey);

    uno::Sequence<sal_Int32> GetWasUsed() const;
    void SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed);
};

//! SvXMLNumUsedList_Impl should be optimized!

SvXMLNumUsedList_Impl::SvXMLNumUsedList_Impl() :
    nUsedCount(0),
    nWasUsedCount(0)
{
}

void SvXMLNumUsedList_Impl::SetUsed( sal_uInt32 nKey )
{
    if ( !IsWasUsed(nKey) )
    {
        std::pair<SvXMLuInt32Set::iterator, bool> aPair = aUsed.insert( nKey );
        if (aPair.second)
            nUsedCount++;
    }
}

bool SvXMLNumUsedList_Impl::IsUsed( sal_uInt32 nKey ) const
{
    SvXMLuInt32Set::const_iterator aItr = aUsed.find(nKey);
    return (aItr != aUsed.end());
}

bool SvXMLNumUsedList_Impl::IsWasUsed( sal_uInt32 nKey ) const
{
    SvXMLuInt32Set::const_iterator aItr = aWasUsed.find(nKey);
    return (aItr != aWasUsed.end());
}

void SvXMLNumUsedList_Impl::Export()
{
    SvXMLuInt32Set::const_iterator aItr = aUsed.begin();
    while (aItr != aUsed.end())
    {
        std::pair<SvXMLuInt32Set::const_iterator, bool> aPair = aWasUsed.insert( *aItr );
        if (aPair.second)
            nWasUsedCount++;
        ++aItr;
    }
    aUsed.clear();
    nUsedCount = 0;
}

bool SvXMLNumUsedList_Impl::GetFirstUsed(sal_uInt32& nKey)
{
    bool bRet(false);
    aCurrentUsedPos = aUsed.begin();
    if(nUsedCount)
    {
        DBG_ASSERT(aCurrentUsedPos != aUsed.end(), "something went wrong");
        nKey = *aCurrentUsedPos;
        bRet = true;
    }
    return bRet;
}

bool SvXMLNumUsedList_Impl::GetNextUsed(sal_uInt32& nKey)
{
    bool bRet(false);
    if (aCurrentUsedPos != aUsed.end())
    {
        ++aCurrentUsedPos;
        if (aCurrentUsedPos != aUsed.end())
        {
            nKey = *aCurrentUsedPos;
            bRet = true;
        }
    }
    return bRet;
}

uno::Sequence<sal_Int32> SvXMLNumUsedList_Impl::GetWasUsed() const
{
    return comphelper::containerToSequence<sal_Int32>(aWasUsed);
}

void SvXMLNumUsedList_Impl::SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed)
{
    DBG_ASSERT(nWasUsedCount == 0, "WasUsed should be empty");
    for (const auto nWasUsed : rWasUsed)
    {
        std::pair<SvXMLuInt32Set::const_iterator, bool> aPair = aWasUsed.insert( nWasUsed );
        if (aPair.second)
            nWasUsedCount++;
    }
}

SvXMLNumFmtExport::SvXMLNumFmtExport(
            SvXMLExport& rExp,
            const uno::Reference< util::XNumberFormatsSupplier >& rSupp ) :
    m_rExport( rExp ),
    m_sPrefix( u"N"_ustr ),
    m_pFormatter( nullptr ),
    m_bHasText( false )
{
    //  supplier must be SvNumberFormatsSupplierObj
    SvNumberFormatsSupplierObj* pObj =
                    comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( rSupp );
    if (pObj)
        m_pFormatter = pObj->GetNumberFormatter();

    if ( m_pFormatter )
    {
        m_pLocaleData = LocaleDataWrapper::get( m_pFormatter->GetLanguageTag() );
    }
    else
    {
        LanguageTag aLanguageTag( MsLangId::getConfiguredSystemLanguage() );

        m_pLocaleData = LocaleDataWrapper::get( std::move(aLanguageTag) );
    }

    m_pUsedList.reset(new SvXMLNumUsedList_Impl);
}

SvXMLNumFmtExport::SvXMLNumFmtExport(
                       SvXMLExport& rExp,
                       const css::uno::Reference< css::util::XNumberFormatsSupplier >& rSupp,
                       OUString aPrefix ) :
    m_rExport( rExp ),
    m_sPrefix(std::move( aPrefix )),
    m_pFormatter( nullptr ),
    m_bHasText( false )
{
    //  supplier must be SvNumberFormatsSupplierObj
    SvNumberFormatsSupplierObj* pObj =
                    comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( rSupp );
    if (pObj)
        m_pFormatter = pObj->GetNumberFormatter();

    if ( m_pFormatter )
    {
        m_pLocaleData = LocaleDataWrapper::get( m_pFormatter->GetLanguageTag() );
    }
    else
    {
        LanguageTag aLanguageTag( MsLangId::getConfiguredSystemLanguage() );

        m_pLocaleData = LocaleDataWrapper::get( std::move(aLanguageTag) );
    }

    m_pUsedList.reset(new SvXMLNumUsedList_Impl);
}

SvXMLNumFmtExport::~SvXMLNumFmtExport()
{
}

//  helper methods

static OUString lcl_CreateStyleName( sal_Int32 nKey, sal_Int32 nPart, bool bDefPart, std::u16string_view rPrefix )
{
    if (bDefPart)
        return rPrefix + OUString::number(nKey);
    else
        return rPrefix + OUString::number(nKey) + "P" + OUString::number( nPart );
}

void SvXMLNumFmtExport::AddCalendarAttr_Impl( const OUString& rCalendar )
{
    if ( !rCalendar.isEmpty() )
    {
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_CALENDAR, rCalendar );
    }
}

void SvXMLNumFmtExport::AddStyleAttr_Impl( bool bLong )
{
    if ( bLong )            // short is default
    {
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_STYLE, XML_LONG );
    }
}

void SvXMLNumFmtExport::AddLanguageAttr_Impl( LanguageType nLang )
{
    if ( nLang != LANGUAGE_SYSTEM )
    {
        m_rExport.AddLanguageTagAttributes( XML_NAMESPACE_NUMBER, XML_NAMESPACE_NUMBER,
                LanguageTag( nLang), false);
    }
}

//  methods to write individual elements within a format

void SvXMLNumFmtExport::AddToTextElement_Impl( std::u16string_view rString )
{
    //  append to sTextContent, write element in FinishTextElement_Impl
    //  to avoid several text elements following each other

    m_sTextContent.append( rString );
    // Also empty string leads to a number:text element as it may separate
    // keywords of the same letter (e.g. MM""MMM) that otherwise would be
    // concatenated when reading back in.
    m_bHasText = true;
}

void SvXMLNumFmtExport::FinishTextElement_Impl(bool bUseExtensionNS)
{
    if ( m_bHasText )
    {
        if ( !m_sBlankWidthString.isEmpty() )
        {
            // Export only for 1.3 with extensions and later.
            SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
            if (eVersion > SvtSaveOptions::ODFSVER_013 && ( (eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0 ))
            {
                m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_BLANK_WIDTH_CHAR,
                                      m_sBlankWidthString.makeStringAndClear() );
            }
        }
        sal_uInt16 nNS = bUseExtensionNS ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER;
        SvXMLElementExport aElem( m_rExport, nNS, XML_TEXT,
                                  truefalse );
        m_rExport.Characters( m_sTextContent.makeStringAndClear() );
        m_bHasText = false;
    }
}

void SvXMLNumFmtExport::WriteColorElement_Impl( const Color& rColor )
{
    FinishTextElement_Impl();

    OUStringBuffer aColStr( 7 );
    ::sax::Converter::convertColor( aColStr, rColor );
    m_rExport.AddAttribute( XML_NAMESPACE_FO, XML_COLOR,
                          aColStr.makeStringAndClear() );

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_STYLE, XML_TEXT_PROPERTIES,
                              truefalse );
}

void SvXMLNumFmtExport::WriteCurrencyElement_Impl( const OUString& rString,
                                                    std::u16string_view rExt )
{
    FinishTextElement_Impl();

    if ( !rExt.empty() )
    {
        // rExt should be a 16-bit hex value max FFFF which may contain a
        // leading "-" separator (that is not a minus sign, but toInt32 can be
        // used to parse it, with post-processing as necessary):
        sal_Int32 nLang = o3tl::toInt32(rExt, 16);
        if ( nLang < 0 )
            nLang = -nLang;
        SAL_WARN_IF(nLang > 0xFFFF, "xmloff.style""Out of range Lang Id: " << nLang << " from input string: " << OUString(rExt));
        AddLanguageAttr_Impl( LanguageType(nLang & 0xFFFF) );          // adds to pAttrList
    }

    SvXMLElementExport aElem( m_rExport,
                              XML_NAMESPACE_NUMBER, XML_CURRENCY_SYMBOL,
                              truefalse );
    m_rExport.Characters( rString );
}

void SvXMLNumFmtExport::WriteBooleanElement_Impl()
{
    FinishTextElement_Impl();

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_BOOLEAN,
                              truefalse );
}

void SvXMLNumFmtExport::WriteTextContentElement_Impl()
{
    FinishTextElement_Impl();

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_TEXT_CONTENT,
                              truefalse );
}

//  date elements

void SvXMLNumFmtExport::WriteDayElement_Impl( const OUString& rCalendar, bool bLong )
{
    FinishTextElement_Impl();

    AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
    AddStyleAttr_Impl( bLong );     // adds to pAttrList

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_DAY,
                              truefalse );
}

void SvXMLNumFmtExport::WriteMonthElement_Impl( const OUString& rCalendar, bool bLong, bool bText )
{
    FinishTextElement_Impl();

    AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
    AddStyleAttr_Impl( bLong );     // adds to pAttrList
    if ( bText )
    {
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TEXTUAL, XML_TRUE );
    }

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_MONTH,
                              truefalse );
}

void SvXMLNumFmtExport::WriteYearElement_Impl( const OUString& rCalendar, bool bLong )
{
    FinishTextElement_Impl();

    AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
    AddStyleAttr_Impl( bLong );     // adds to pAttrList

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_YEAR,
                              truefalse );
}

void SvXMLNumFmtExport::WriteEraElement_Impl( const OUString& rCalendar, bool bLong )
{
    FinishTextElement_Impl();

    AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
    AddStyleAttr_Impl( bLong );     // adds to pAttrList

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_ERA,
                              truefalse );
}

void SvXMLNumFmtExport::WriteDayOfWeekElement_Impl( const OUString& rCalendar, bool bLong )
{
    FinishTextElement_Impl();

    AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
    AddStyleAttr_Impl( bLong );     // adds to pAttrList

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_DAY_OF_WEEK,
                              truefalse );
}

void SvXMLNumFmtExport::WriteWeekElement_Impl( const OUString& rCalendar )
{
    FinishTextElement_Impl();

    AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_WEEK_OF_YEAR,
                              truefalse );
}

void SvXMLNumFmtExport::WriteQuarterElement_Impl( const OUString& rCalendar, bool bLong )
{
    FinishTextElement_Impl();

    AddCalendarAttr_Impl( rCalendar ); // adds to pAttrList
    AddStyleAttr_Impl( bLong );     // adds to pAttrList

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_QUARTER,
                              truefalse );
}

//  time elements

void SvXMLNumFmtExport::WriteHoursElement_Impl( bool bLong )
{
    FinishTextElement_Impl();

    AddStyleAttr_Impl( bLong );     // adds to pAttrList

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_HOURS,
                              truefalse );
}

void SvXMLNumFmtExport::WriteMinutesElement_Impl( bool bLong )
{
    FinishTextElement_Impl();

    AddStyleAttr_Impl( bLong );     // adds to pAttrList

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_MINUTES,
                              truefalse );
}

void SvXMLNumFmtExport::WriteRepeatedElement_Impl( sal_Unicode nChar )
{
    // Export only for 1.2 with extensions or 1.3 and later.
    SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
    if (eVersion > SvtSaveOptions::ODFSVER_012)
    {
        FinishTextElement_Impl(eVersion < SvtSaveOptions::ODFSVER_013);
        // OFFICE-3765 For 1.2+ use loext namespace, for 1.3 use number namespace.
        SvXMLElementExport aElem( m_rExport,
                                  ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
                                  XML_FILL_CHARACTER, truefalse );
        m_rExport.Characters( OUString( nChar ) );
    }
}

namespace {
void lcl_WriteBlankWidthString( std::u16string_view rBlankWidthChar, OUStringBuffer&&nbsp;rBlankWidthString, OUStringBuffer& rTextContent )
{
    // export "_x"
    if ( rBlankWidthString.isEmpty() )
    {
        rBlankWidthString.append( rBlankWidthChar );
        if ( !rTextContent.isEmpty() )
        {
            // add position in rTextContent
            rBlankWidthString.append( rTextContent.getLength() );
        }
    }
    else
    {
        // add "_" as separator if there are several blank width char
        rBlankWidthString.append( "_" );
        rBlankWidthString.append( rBlankWidthChar );
        rBlankWidthString.append( rTextContent.getLength() );
    }
    // for previous versions, turn "_x" into the number of spaces used for x in InsertBlanks in the NumberFormat
    if ( !rBlankWidthChar.empty() )
    {
        OUString aBlanks;
        SvNumberformat::InsertBlanks( aBlanks, 0, rBlankWidthChar[0] );
        rTextContent.append( aBlanks );
    }
}
}

void SvXMLNumFmtExport::WriteSecondsElement_Impl( bool bLong, sal_uInt16 nDecimals )
{
    FinishTextElement_Impl();

    AddStyleAttr_Impl( bLong );     // adds to pAttrList
    if ( nDecimals > 0 )
    {
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
                              OUString::number(  nDecimals ) );
    }

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_SECONDS,
                              truefalse );
}

void SvXMLNumFmtExport::WriteAMPMElement_Impl()
{
    FinishTextElement_Impl();

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_AM_PM,
                              truefalse );
}

//  numbers

void SvXMLNumFmtExport::WriteIntegerElement_Impl(
                            sal_Int32 nInteger, sal_Int32 nBlankInteger, bool bGrouping )
{
    //  integer digits: '0' and '?'
    if ( nInteger >= 0 )    // negative = automatic
    {
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_INTEGER_DIGITS,
                              OUString::number( nInteger ) );
    }
    SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
    //  blank integer digits: '?'
    if ( nBlankInteger > 0 && ( (eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0 ) )
    {
        m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_MAX_BLANK_INTEGER_DIGITS,
                              OUString::number( nBlankInteger ) );
    }
    //  (automatic) grouping separator
    if ( bGrouping )
    {
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_GROUPING, XML_TRUE );
    }
}

void SvXMLNumFmtExport::WriteEmbeddedEntries_Impl( const SvXMLEmbeddedTextEntryArr&&nbsp;rEmbeddedEntries )
{
    auto nEntryCount = rEmbeddedEntries.size();
    SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
    for (decltype(nEntryCount) nEntry=0; nEntry < nEntryCount; ++nEntry)
    {
        const SvXMLEmbeddedTextEntry* pObj = &rEmbeddedEntries[nEntry];

        //  position attribute
        // position == 0 is between first integer digit and decimal separator
        // position < 0 is inside decimal part
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_POSITION,
                                OUString::number( pObj->nFormatPos ) );

        //  text as element content
        OUStringBuffer aContent;
        OUStringBuffer aBlankWidthString;
        do
        {
            pObj = &rEmbeddedEntries[nEntry];
            if ( pObj->isBlankWidth  )
            {
                //  (#i20396# the spaces may also be in embedded-text elements)
                lcl_WriteBlankWidthString( pObj->aText, aBlankWidthString, aContent );
            }
            else
            {
                // The array can contain several elements for the same position in the number.
                // Literal texts are merged into a single embedded-text element.
                aContent.append( pObj->aText );
            }
            ++nEntry;
        }
        while ( nEntry < nEntryCount
            && rEmbeddedEntries[nEntry].nFormatPos == pObj->nFormatPos );
        --nEntry;

        // Export only for 1.3 with extensions and later.
        if ( !aBlankWidthString.isEmpty() && eVersion > SvtSaveOptions::ODFSVER_013 && ( (eVersion & ;SvtSaveOptions::ODFSVER_EXTENDED) != 0 ) )
            m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_BLANK_WIDTH_CHAR, aBlankWidthString.makeStringAndClear() );
        SvXMLElementExport aChildElem( m_rExport, XML_NAMESPACE_NUMBER, XML_EMBEDDED_TEXT,
                                          truefalse );
        m_rExport.Characters( aContent.makeStringAndClear() );
    }
}

void SvXMLNumFmtExport::WriteNumberElement_Impl(
                            sal_Int32 nDecimals, sal_Int32 nMinDecimals,
                            sal_Int32 nInteger, sal_Int32 nBlankInteger, const OUString& rDashStr,
                            bool bGrouping, sal_Int32 nTrailingThousands,
                            const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries )
{
    FinishTextElement_Impl();

    //  decimals
    if ( nDecimals >= 0 )   // negative = automatic
    {
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
                              OUString::number( nDecimals ) );
    }

    if ( nMinDecimals >= 0 )   // negative = automatic
    {
        // Export only for 1.2 with extensions or 1.3 and later.
        SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
        if (eVersion > SvtSaveOptions::ODFSVER_012)
        {
            // OFFICE-3860 For 1.2+ use loext namespace, for 1.3 use number namespace.
            m_rExport.AddAttribute(
                ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
                                 XML_MIN_DECIMAL_PLACES,
                                 OUString::number( nMinDecimals ) );
        }
    }
    //  decimal replacement (dashes) or variable decimals (#)
    if ( !rDashStr.isEmpty() ||  nMinDecimals < nDecimals )
    {
        // full variable decimals means an empty replacement string
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_REPLACEMENT,
                              rDashStr );
    }

    WriteIntegerElement_Impl( nInteger, nBlankInteger, bGrouping );

    //  display-factor if there are trailing thousands separators
    if ( nTrailingThousands )
    {
        //  each separator character removes three digits
        double fFactor = ::rtl::math::pow10Exp( 1.0, 3 * nTrailingThousands );

        OUStringBuffer aFactStr;
        ::sax::Converter::convertDouble( aFactStr, fFactor );
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DISPLAY_FACTOR, aFactStr.makeStringAndClear() );
    }

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_NUMBER,
                              truetrue );

    //  number:embedded-text as child elements
    WriteEmbeddedEntries_Impl( rEmbeddedEntries );
}

void SvXMLNumFmtExport::WriteScientificElement_Impl(
                            sal_Int32 nDecimals, sal_Int32 nMinDecimals, sal_Int32 nInteger, sal_Int32 nBlankInteger,
                            bool bGrouping, sal_Int32 nExp, sal_Int32 nExpInterval, bool bExpSign, bool bExponentLowercase, sal_Int32 nBlankExp,
                            const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries )
{
    FinishTextElement_Impl();

    //  decimals
    if ( nDecimals >= 0 )   // negative = automatic
    {
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DECIMAL_PLACES,
                              OUString::number( nDecimals ) );
    }

    SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
    if ( nMinDecimals >= 0 )   // negative = automatic
    {
        // Export only for 1.2 with extensions or 1.3 and later.
        if (eVersion > SvtSaveOptions::ODFSVER_012)
        {
            // OFFICE-3860 For 1.2+ use loext namespace, for 1.3 use number namespace.
            m_rExport.AddAttribute(
                ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
                                 XML_MIN_DECIMAL_PLACES,
                                 OUString::number( nMinDecimals ) );
        }
    }

    WriteIntegerElement_Impl( nInteger, nBlankInteger, bGrouping );

    //  exponent digits
    if ( nExp >= 0 )
    {
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_EXPONENT_DIGITS,
                              OUString::number( nExp ) );
    }

    //  exponent interval for engineering notation
    if ( nExpInterval >= 0 )
    {
        // Export only for 1.2 with extensions or 1.3 and later.
        if (eVersion > SvtSaveOptions::ODFSVER_012)
        {
            // OFFICE-1828 For 1.2+ use loext namespace, for 1.3 use number namespace.
            m_rExport.AddAttribute(
                    ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
                    XML_EXPONENT_INTERVAL, OUString::number( nExpInterval ) );
        }
    }

    //  exponent sign
    // Export only for 1.2 with extensions or 1.3 and later.
    if (eVersion > SvtSaveOptions::ODFSVER_012)
    {
        // OFFICE-3860 For 1.2+ use loext namespace, for 1.3 use number namespace.
        m_rExport.AddAttribute(
            ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
                             XML_FORCED_EXPONENT_SIGN,
                             bExpSign? XML_TRUE : XML_FALSE );
    }
    //  exponent string
    // Export only for 1.x with extensions
    if (eVersion & SvtSaveOptions::ODFSVER_EXTENDED)
    {
        if (bExponentLowercase)
            m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_EXPONENT_LOWERCASE, XML_TRUE );
        if (nBlankExp > 0)
        {
            if (nBlankExp >= nExp)
                nBlankExp = nExp - 1; // preserve at least one '0' in exponent
            m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_BLANK_EXPONENT_DIGITS, OUString::number( nBlankExp ) );
        }
    }

    SvXMLElementExport aElem( m_rExport,
                              XML_NAMESPACE_NUMBER, XML_SCIENTIFIC_NUMBER,
                              truefalse );

    //  number:embedded-text as child elements
    // Export only for 1.x with extensions
    if (eVersion & SvtSaveOptions::ODFSVER_EXTENDED)
        WriteEmbeddedEntries_Impl( rEmbeddedEntries );
}

void SvXMLNumFmtExport::WriteFractionElement_Impl(
                            sal_Int32 nInteger, sal_Int32 nBlankInteger, bool bGrouping,
                            const SvNumberformat& rFormat, sal_uInt16 nPart )
{
    FinishTextElement_Impl();
    WriteIntegerElement_Impl( nInteger, nBlankInteger, bGrouping );

    const OUString aNumeratorString = rFormat.GetNumeratorString( nPart );
    const OUString aDenominatorString = rFormat.GetDenominatorString( nPart );
    const OUString aIntegerFractionDelimiterString = rFormat.GetIntegerFractionDelimiterString( nPart );
    sal_Int32 nMaxNumeratorDigits = aNumeratorString.getLength();
    // Count '0' as '?'
    sal_Int32 nMinNumeratorDigits = aNumeratorString.replaceAll("0","?").indexOf('?');
    sal_Int32 nZerosNumeratorDigits = aNumeratorString.indexOf('0');
    if ( nMinNumeratorDigits >= 0 )
        nMinNumeratorDigits = nMaxNumeratorDigits - nMinNumeratorDigits;
    else
        nMinNumeratorDigits = 0;
    if ( nZerosNumeratorDigits >= 0 )
        nZerosNumeratorDigits = nMaxNumeratorDigits - nZerosNumeratorDigits;
    else
        nZerosNumeratorDigits = 0;
    sal_Int32 nMaxDenominatorDigits = aDenominatorString.getLength();
    sal_Int32 nMinDenominatorDigits = aDenominatorString.replaceAll("0","?").indexOf('?');
    sal_Int32 nZerosDenominatorDigits = aDenominatorString.indexOf('0');
    if ( nMinDenominatorDigits >= 0 )
        nMinDenominatorDigits = nMaxDenominatorDigits - nMinDenominatorDigits;
    else
        nMinDenominatorDigits = 0;
    if ( nZerosDenominatorDigits >= 0 )
        nZerosDenominatorDigits = nMaxDenominatorDigits - nZerosDenominatorDigits;
    else
        nZerosDenominatorDigits = 0;
    sal_Int32 nDenominator = aDenominatorString.toInt32();

    SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();

    // integer/fraction delimiter
    if ( !aIntegerFractionDelimiterString.isEmpty() && aIntegerFractionDelimiterString != " "
        && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0) )
    {   // Export only for 1.2/1.3 with extensions.
        m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_INTEGER_FRACTION_DELIMITER,
                              aIntegerFractionDelimiterString );
    }

    //  numerator digits
    if ( nMinNumeratorDigits == 0 ) // at least one digit to keep compatibility with previous versions
        nMinNumeratorDigits++;
    m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_NUMERATOR_DIGITS,
                          OUString::number( nMinNumeratorDigits ) );
    // Export only for 1.2/1.3 with extensions.
    if ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0)
    {
        // For extended ODF use loext namespace
        m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_MAX_NUMERATOR_DIGITS,
                              OUString::number( nMaxNumeratorDigits ) );
    }
    if ( nZerosNumeratorDigits && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0) )
        m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_ZEROS_NUMERATOR_DIGITS,
                              OUString::number( nZerosNumeratorDigits ) );

    if ( nDenominator )
    {
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_DENOMINATOR_VALUE,
                              OUString::number( nDenominator) );
    }
    //  it's not necessary to export nDenominatorDigits
    //  if we have a forced denominator
    else
    {
        if ( nMinDenominatorDigits == 0 ) // at least one digit to keep compatibility with previous versions
            nMinDenominatorDigits++;
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_MIN_DENOMINATOR_DIGITS,
                              OUString::number( nMinDenominatorDigits ) );
        if (eVersion > SvtSaveOptions::ODFSVER_012)
        {
            // OFFICE-3695 For 1.2+ use loext namespace, for 1.3 use number namespace.
            m_rExport.AddAttribute(
                ((eVersion < SvtSaveOptions::ODFSVER_013) ? XML_NAMESPACE_LO_EXT : XML_NAMESPACE_NUMBER),
                                 XML_MAX_DENOMINATOR_VALUE,
                                 OUString::number( pow ( 10.0, nMaxDenominatorDigits ) - 1 ) ); // 9, 99 or 999
        }
        if ( nZerosDenominatorDigits && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0) )
            m_rExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_ZEROS_DENOMINATOR_DIGITS,
                                  OUString::number( nZerosDenominatorDigits ) );
    }

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_FRACTION,
                              truefalse );
}

//  mapping (condition)

void SvXMLNumFmtExport::WriteMapElement_Impl( sal_Int32 nOp, double fLimit,
                                                sal_Int32 nKey, sal_Int32 nPart )
{
    FinishTextElement_Impl();

    if ( nOp == NUMBERFORMAT_OP_NO )
        return;

    // style namespace

    OUStringBuffer aCondStr(20);
    aCondStr.append( "value()" );          //! define constant
    switch ( nOp )
    {
        case NUMBERFORMAT_OP_EQ: aCondStr.append( '=' );  break;
        case NUMBERFORMAT_OP_NE: aCondStr.append( "!=" );          break;
        case NUMBERFORMAT_OP_LT: aCondStr.append( '<' );  break;
        case NUMBERFORMAT_OP_LE: aCondStr.append( "<=" );          break;
        case NUMBERFORMAT_OP_GT: aCondStr.append( '>' );  break;
        case NUMBERFORMAT_OP_GE: aCondStr.append( ">=" );          break;
        default:
            OSL_FAIL("unknown operator");
    }
    ::rtl::math::doubleToUStringBuffer( aCondStr, fLimit,
            rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
            '.'true );

    m_rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_CONDITION,
                          aCondStr.makeStringAndClear() );

    m_rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_APPLY_STYLE_NAME,
                          m_rExport.EncodeStyleName( lcl_CreateStyleName( nKey, nPart, false,
                                               m_sPrefix ) ) );

    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_STYLE, XML_MAP,
                              truefalse );
}

//  for old (automatic) currency formats: parse currency symbol from text

static sal_Int32 lcl_FindSymbol( const OUString& sUpperStr, std::u16string_view sCurString )
{
    //  search for currency symbol
    //  Quoting as in ImpSvNumberformatScan::Symbol_Division

    sal_Int32 nCPos = 0;
    while (nCPos >= 0)
    {
        nCPos = sUpperStr.indexOf( sCurString, nCPos );
        if (nCPos >= 0)
        {
            // in Quotes?
            sal_Int32 nQ = SvNumberformat::GetQuoteEnd( sUpperStr, nCPos );
            if ( nQ < 0 )
            {
                //  dm can be escaped as "dm or \d
                sal_Unicode c;
                if ( nCPos == 0 )
                    return nCPos;                   // found
                c = sUpperStr[nCPos-1];
                if ( c != '"' && c != '\\')
                {
                    return nCPos;                   // found
                }
                else
                {
                    nCPos++;                        // continue
                }
            }
            else
            {
                nCPos = nQ + 1;                     // continue after quote end
            }
        }
    }
    return -1;
}

bool SvXMLNumFmtExport::WriteTextWithCurrency_Impl( const OUString& rString,
                            const css::lang::Locale& rLocale )
{
    //  returns true if currency element was written

    bool bRet = false;

    LanguageTag aLanguageTag( rLocale );
    m_pFormatter->ChangeIntl( aLanguageTag.getLanguageType( false) );
    OUString sCurString, sDummy;
    m_pFormatter->GetCompatibilityCurrency( sCurString, sDummy );

    OUString sUpperStr = m_pFormatter->GetCharClass()->uppercase(rString);
    sal_Int32 nPos = lcl_FindSymbol( sUpperStr, sCurString );
    if ( nPos >= 0 )
    {
        sal_Int32 nLength = rString.getLength();
        sal_Int32 nCurLen = sCurString.getLength();
        sal_Int32 nCont = nPos + nCurLen;

        //  text before currency symbol
        if ( nPos > 0 )
        {
            AddToTextElement_Impl( rString.subView( 0, nPos ) );
        }
        //  currency symbol (empty string -> default)
        WriteCurrencyElement_Impl( u""_ustr, u"" );
        bRet = true;

        //  text after currency symbol
        if ( nCont < nLength )
        {
            AddToTextElement_Impl( rString.subView( nCont, nLength-nCont ) );
        }
    }
    else
    {
        AddToTextElement_Impl( rString );       // simple text
    }

    return bRet;        // true: currency element written
}

static OUString lcl_GetDefaultCalendar( SvNumberFormatter const * pFormatter, LanguageType nLang )
{
    //  get name of first non-gregorian calendar for the language

    OUString aCalendar;
    CalendarWrapper* pCalendar = pFormatter->GetCalendar();
    if (pCalendar)
    {
        lang::Locale aLocale( LanguageTag::convertToLocale( nLang ) );

        const uno::Sequence<OUString> aCals = pCalendar->getAllCalendars( aLocale );
        auto pCal = std::find_if(aCals.begin(), aCals.end(),
            [](const OUString& rCal) { return rCal != "gregorian"; });
        if (pCal != aCals.end())
            aCalendar = *pCal;
    }
    return aCalendar;
}

static bool lcl_IsInEmbedded( const SvXMLEmbeddedTextEntryArr& rEmbeddedEntries, sal_uInt16 nPos )
{
    auto nCount = rEmbeddedEntries.size();
    for (decltype(nCount) i=0; i<nCount; i++)
        if ( rEmbeddedEntries[i].nSourcePos == nPos )
            return true;

    return false;       // not found
}

static bool lcl_IsDefaultDateFormat( const SvNumberformat& rFormat, bool bSystemDate, NfIndexTableOffset eBuiltIn )
{
    //  make an extra loop to collect date elements, to check if it is a default format
    //  before adding the automatic-order attribute

    SvXMLDateElementAttributes eDateDOW = XML_DEA_NONE;
    SvXMLDateElementAttributes eDateDay = XML_DEA_NONE;
    SvXMLDateElementAttributes eDateMonth = XML_DEA_NONE;
    SvXMLDateElementAttributes eDateYear = XML_DEA_NONE;
    SvXMLDateElementAttributes eDateHours = XML_DEA_NONE;
    SvXMLDateElementAttributes eDateMins = XML_DEA_NONE;
    SvXMLDateElementAttributes eDateSecs = XML_DEA_NONE;
    bool bDateNoDefault = false;

    sal_uInt16 nPos = 0;
    bool bEnd = false;
    short nLastType = 0;
    while (!bEnd)
    {
        short nElemType = rFormat.GetNumForType( 0, nPos );
        switch ( nElemType )
        {
            case 0:
                if ( nLastType == NF_SYMBOLTYPE_STRING )
                    bDateNoDefault = true;  // text at the end -> no default date format
                bEnd = true;                // end of format reached
                break;
            case NF_SYMBOLTYPE_STRING:
            case NF_SYMBOLTYPE_DATESEP:
            case NF_SYMBOLTYPE_TIMESEP:
            case NF_SYMBOLTYPE_TIME100SECSEP:
                // text is ignored, except at the end
                break;
            // same mapping as in SvXMLNumFormatContext::AddNfKeyword:
            case NF_KEY_NN:     eDateDOW = XML_DEA_SHORT;       break;
            case NF_KEY_NNN:
            case NF_KEY_NNNN:   eDateDOW = XML_DEA_LONG;        break;
            case NF_KEY_D:      eDateDay = XML_DEA_SHORT;       break;
            case NF_KEY_DD:     eDateDay = XML_DEA_LONG;        break;
            case NF_KEY_M:      eDateMonth = XML_DEA_SHORT;     break;
            case NF_KEY_MM:     eDateMonth = XML_DEA_LONG;      break;
            case NF_KEY_MMM:    eDateMonth = XML_DEA_TEXTSHORT; break;
            case NF_KEY_MMMM:   eDateMonth = XML_DEA_TEXTLONG;  break;
            case NF_KEY_YY:     eDateYear = XML_DEA_SHORT;      break;
            case NF_KEY_YYYY:   eDateYear = XML_DEA_LONG;       break;
            case NF_KEY_H:      eDateHours = XML_DEA_SHORT;     break;
            case NF_KEY_HH:     eDateHours = XML_DEA_LONG;      break;
            case NF_KEY_MI:     eDateMins = XML_DEA_SHORT;      break;
            case NF_KEY_MMI:    eDateMins = XML_DEA_LONG;       break;
            case NF_KEY_S:      eDateSecs = XML_DEA_SHORT;      break;
            case NF_KEY_SS:     eDateSecs = XML_DEA_LONG;       break;
            case NF_KEY_AP:
            case NF_KEY_AMPM:   break;          // AM/PM may or may not be in date/time formats -> ignore by itself
            default:
                bDateNoDefault = true;      // any other element -> no default format
        }
        nLastType = nElemType;
        ++nPos;
    }

    if ( bDateNoDefault )
        return false;                       // additional elements
    else
    {
        NfIndexTableOffset eFound = static_cast<NfIndexTableOffset>(SvXMLNumFmtDefaults::GetDefaultDateFormat(
                eDateDOW, eDateDay, eDateMonth, eDateYear, eDateHours, eDateMins, eDateSecs, bSystemDate ));

        return ( eFound == eBuiltIn );
    }
}

//  export one part (condition)

void SvXMLNumFmtExport::ExportPart_Impl( const SvNumberformat& rFormat, sal_uInt32 nKey, sal_uInt32 nRealKey,
                                            sal_uInt16 nPart, bool bDefPart )
{
    //! for the default part, pass the conditions from the other parts!

    //  element name

    NfIndexTableOffset eBuiltIn = SvNumberFormatter::GetIndexTableOffset( nRealKey );

    SvNumFormatType nFmtType = SvNumFormatType::ALL;
    bool bThousand = false;
    sal_uInt16 nPrecision = 0;
    sal_uInt16 nLeading = 0;
    rFormat.GetNumForInfo( nPart, nFmtType, bThousand, nPrecision, nLeading);
    nFmtType &= ~SvNumFormatType::DEFINED;

    //  special treatment of builtin formats that aren't detected by normal parsing
    //  (the same formats that get the type set in SvNumberFormatter::ImpGenerateFormats)
    if ( eBuiltIn == NF_NUMBER_STANDARD )
        nFmtType = SvNumFormatType::NUMBER;
    else if ( eBuiltIn == NF_BOOLEAN )
        nFmtType = SvNumFormatType::LOGICAL;
    else if ( eBuiltIn == NF_TEXT )
        nFmtType = SvNumFormatType::TEXT;

    // #101606# An empty subformat is a valid number-style resulting in an
    // empty display string for the condition of the subformat.

    XMLTokenEnum eType = XML_TOKEN_INVALID;
    switch ( nFmtType )
    {
        // Type UNDEFINED likely is a crappy format string for that we could
        // not decide on any format type (and maybe could try harder?), but the
        // resulting XMLTokenEnum should be something valid, so make that
        // number-style.
        case SvNumFormatType::UNDEFINED:
            SAL_WARN("xmloff.style","UNDEFINED number format: '" << rFormat.GetFormatstring() << "'");
            [[fallthrough]];
        // Type is 0 if a format contains no recognized elements
        // (like text only) - this is handled as a number-style.
        case SvNumFormatType::ALL:
        case SvNumFormatType::EMPTY:
        case SvNumFormatType::NUMBER:
        case SvNumFormatType::SCIENTIFIC:
        case SvNumFormatType::FRACTION:
            eType = XML_NUMBER_STYLE;
            break;
        case SvNumFormatType::PERCENT:
            eType = XML_PERCENTAGE_STYLE;
            break;
        case SvNumFormatType::CURRENCY:
            eType = XML_CURRENCY_STYLE;
            break;
        case SvNumFormatType::DATE:
        case SvNumFormatType::DATETIME:
            eType = XML_DATE_STYLE;
            break;
        case SvNumFormatType::TIME:
            eType = XML_TIME_STYLE;
            break;
        case SvNumFormatType::TEXT:
            eType = XML_TEXT_STYLE;
            break;
        case SvNumFormatType::LOGICAL:
            eType = XML_BOOLEAN_STYLE;
            break;
        defaultbreak;
    }
    SAL_WARN_IF( eType == XML_TOKEN_INVALID, "xmloff.style""unknown format type" );

    OUString sAttrValue;
    bool bUserDef( rFormat.GetType() & SvNumFormatType::DEFINED );

    //  common attributes for format

    //  format name (generated from key) - style namespace
    m_rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_NAME,
                        lcl_CreateStyleName( nKey, nPart, bDefPart, m_sPrefix ) );

    //  "volatile" attribute for styles used only in maps
    if ( !bDefPart )
        m_rExport.AddAttribute( XML_NAMESPACE_STYLE, XML_VOLATILE, XML_TRUE );

    //  language / country
    LanguageType nLang = rFormat.GetLanguage();
    AddLanguageAttr_Impl( nLang );                  // adds to pAttrList

    //  title (comment)
    //  titles for builtin formats are not written
    sAttrValue = rFormat.GetComment();
    if ( !sAttrValue.isEmpty() && bUserDef && bDefPart )
    {
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TITLE, sAttrValue );
    }

    //  automatic ordering for currency and date formats
    //  only used for some built-in formats
    bool bAutoOrder = ( eBuiltIn == NF_CURRENCY_1000INT     || eBuiltIn == NF_CURRENCY_1000DEC2 ||
                        eBuiltIn == NF_CURRENCY_1000INT_RED || eBuiltIn == NF_CURRENCY_1000DEC2_RED ||
                        eBuiltIn == NF_CURRENCY_1000DEC2_DASHED ||
                        eBuiltIn == NF_DATE_SYSTEM_SHORT    || eBuiltIn == NF_DATE_SYSTEM_LONG ||
                        eBuiltIn == NF_DATE_SYS_MMYY        || eBuiltIn == NF_DATE_SYS_DDMMM ||
                        eBuiltIn == NF_DATE_SYS_DDMMYYYY    || eBuiltIn == NF_DATE_SYS_DDMMYY ||
                        eBuiltIn == NF_DATE_SYS_DMMMYY      || eBuiltIn == NF_DATE_SYS_DMMMYYYY ||
                        eBuiltIn == NF_DATE_SYS_DMMMMYYYY   || eBuiltIn == NF_DATE_SYS_NNDMMMYY ||
                        eBuiltIn == NF_DATE_SYS_NNDMMMMYYYY || eBuiltIn == NF_DATE_SYS_NNNNDMMMMYYYY ||
                        eBuiltIn == NF_DATETIME_SYSTEM_SHORT_HHMM || eBuiltIn == NF_DATETIME_SYS_DDMMYYYY_HHMM ||
                        eBuiltIn == NF_DATETIME_SYS_DDMMYYYY_HHMMSS );

    //  format source (for date and time formats)
    //  only used for some built-in formats
    bool bSystemDate = ( eBuiltIn == NF_DATE_SYSTEM_SHORT ||
                         eBuiltIn == NF_DATE_SYSTEM_LONG  ||
                         eBuiltIn == NF_DATETIME_SYSTEM_SHORT_HHMM );
    bool bLongSysDate = ( eBuiltIn == NF_DATE_SYSTEM_LONG );

    // check if the format definition matches the key
    if ( bAutoOrder && ( nFmtType == SvNumFormatType::DATE || nFmtType == SvNumFormatType::DATETIME ) &&
            !lcl_IsDefaultDateFormat( rFormat, bSystemDate, eBuiltIn ) )
    {
        bAutoOrder = bSystemDate = bLongSysDate = false;        // don't write automatic-order attribute then
    }

    if ( bAutoOrder &&
        ( nFmtType == SvNumFormatType::CURRENCY || nFmtType == SvNumFormatType::DATE || nFmtType == SvNumFormatType::DATETIME ) )
    {
        //  #85109# format type must be checked to avoid dtd errors if
        //  locale data contains other format types at the built-in positions

        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_AUTOMATIC_ORDER,
                              XML_TRUE );
    }

    if ( bSystemDate && bAutoOrder &&
        ( nFmtType == SvNumFormatType::DATE || nFmtType == SvNumFormatType::DATETIME ) )
    {
        //  #85109# format type must be checked to avoid dtd errors if
        //  locale data contains other format types at the built-in positions

        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_FORMAT_SOURCE,
                              XML_LANGUAGE );
    }

    //  overflow for time formats as in [hh]:mm
    //  controlled by bThousand from number format info
    //  default for truncate-on-overflow is true
    if ( nFmtType == SvNumFormatType::TIME && bThousand )
    {
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRUNCATE_ON_OVERFLOW,
                              XML_FALSE );
    }

    // Native number transliteration
    css::i18n::NativeNumberXmlAttributes2 aAttr;
    rFormat.GetNatNumXml( aAttr, nPart, m_pFormatter->GetNatNum() );
    if ( !aAttr.Format.isEmpty() )
    {
        assert(aAttr.Spellout.isEmpty());   // mutually exclusive

        /* FIXME-BCP47: ODF defines no transliteration-script or
         * transliteration-rfc-language-tag */

        LanguageTag aLanguageTag( aAttr.Locale);
        OUString aLanguage, aScript, aCountry;
        aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_FORMAT,
                              aAttr.Format );
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_LANGUAGE,
                              aLanguage );
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_COUNTRY,
                              aCountry );
        m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_STYLE,
                              aAttr.Style );
    }

    SvtSaveOptions::ODFSaneDefaultVersion eVersion = m_rExport.getSaneDefaultVersion();
    if ( !aAttr.Spellout.isEmpty() )
    {
        const bool bWriteSpellout = aAttr.Format.isEmpty();
        assert(bWriteSpellout);     // mutually exclusive

        // Export only for 1.2 and later with extensions
        // Also ensure that duplicated transliteration-language and
        // transliteration-country attributes never escape into the wild with
        // releases.
        if ( (eVersion & SvtSaveOptions::ODFSVER_EXTENDED) && bWriteSpellout )
        {
            /* FIXME-BCP47: ODF defines no transliteration-script or
             * transliteration-rfc-language-tag */

            LanguageTag aLanguageTag( aAttr.Locale);
            OUString aLanguage, aScript, aCountry;
            aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
            // For 1.2/1.3+ use loext namespace.
            m_rExport.AddAttribute( /*((eVersion < SvtSaveOptions::ODFSVER_)
                        ? */

                    XML_TRANSLITERATION_SPELLOUT, aAttr.Spellout );
            m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_LANGUAGE,
                                  aLanguage );
            m_rExport.AddAttribute( XML_NAMESPACE_NUMBER, XML_TRANSLITERATION_COUNTRY,
                                  aCountry );
        }
    }

    // The element
    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, eType,
                              truetrue );

    //  color (properties element)

    const Color* pCol = rFormat.GetColor( nPart );
    if (pCol)
        WriteColorElement_Impl(*pCol);

    //  detect if there is "real" content, excluding color and maps
    //! move to implementation of Write... methods?
    bool bAnyContent = false;

    //  format elements

    SvXMLEmbeddedTextEntryArr aEmbeddedEntries;
    if ( eBuiltIn == NF_NUMBER_STANDARD )
    {
        //  default number format contains just one number element
        WriteNumberElement_Impl( -1, -1, 1, -1, OUString(), false, 0, aEmbeddedEntries );
        bAnyContent = true;
    }
    else if ( eBuiltIn == NF_BOOLEAN )
    {
        //  boolean format contains just one boolean element
        WriteBooleanElement_Impl();
        bAnyContent = true;
    }
    else if (eType == XML_BOOLEAN_STYLE)
    {
        // <number:boolean-style> may contain only <number:boolean> and
        // <number:text> elements.
        sal_uInt16 nPos = 0;
        bool bEnd = false;
        while (!bEnd)
        {
            const short nElemType = rFormat.GetNumForType( nPart, nPos );
            switch (nElemType)
            {
                case 0:
                    bEnd = true;                // end of format reached
                    if (m_bHasText && m_sTextContent.isEmpty())
                        m_bHasText = false;       // don't write trailing empty text
                break;
                case NF_SYMBOLTYPE_STRING:
                    {
                        const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );
                        if (pElemStr)
                            AddToTextElement_Impl( *pElemStr );
                    }
                break;
                case NF_KEY_BOOLEAN:
                    WriteBooleanElement_Impl();
                    bAnyContent = true;
                break;
            }
            ++nPos;
        }
    }
    else
    {
        //  first loop to collect attributes

        bool bDecDashes  = false;
        bool bExpFound   = false;
        bool bCurrFound  = false;
        bool bInInteger  = true;
        bool bExpSign = true;
        bool bExponentLowercase = false;        // 'e' or 'E' for scientific notation
        bool bDecAlign   = false;               // decimal alignment with "?"
        sal_Int32 nExpDigits = 0;               // '0' and '?' in exponent
        sal_Int32 nBlankExp = 0;                // only '?' in exponent
        sal_Int32 nIntegerSymbols = 0;          // for embedded-text, including "#"
        sal_Int32 nTrailingThousands = 0;       // thousands-separators after all digits
        sal_Int32 nMinDecimals = nPrecision;
        sal_Int32 nBlankInteger = 0;
        OUString sCurrExt;
        OUString aCalendar;
        bool bImplicitOtherCalendar = false;
        bool bExplicitCalendar = false;
        sal_uInt16 nPos = 0;
        bool bEnd = false;
        while (!bEnd)
        {
            short nElemType = rFormat.GetNumForType( nPart, nPos );
            const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );

            switch ( nElemType )
            {
                case 0:
                    bEnd = true;                // end of format reached
                    break;
                case NF_SYMBOLTYPE_DIGIT:
                    if ( bExpFound && pElemStr )
                    {
                        nExpDigits += pElemStr->getLength();
                        for ( sal_Int32 i = pElemStr->getLength()-1; i >= 0 ; i-- )
                        {
                            if ( (*pElemStr)[i] == '?' )
                                nBlankExp ++;
                        }
                    }
                    else if ( !bDecDashes && pElemStr && (*pElemStr)[0] == '-' )
                    {
                        bDecDashes = true;
                        nMinDecimals = 0;
                    }
                    else if ( nFmtType != SvNumFormatType::FRACTION && !bInInteger && pElemStr )
                    {
                        for ( sal_Int32 i = pElemStr->getLength()-1; i >= 0 ; i-- )
                        {
                            sal_Unicode aChar = (*pElemStr)[i];
                            if ( aChar == '#' || aChar == '?' )
                            {
                                nMinDecimals --;
                                if ( aChar == '?' )
                                    bDecAlign = true;
                            }
                            else
                                break;
                        }
                    }
                    if ( bInInteger && pElemStr )
                    {
                        nIntegerSymbols += pElemStr->getLength();
                        for ( sal_Int32 i = pElemStr->getLength()-1; i >= 0 ; i-- )
                        {
                            if ( (*pElemStr)[i] == '?' )
                                nBlankInteger ++;
                        }
                    }
                    nTrailingThousands = 0;
                    break;
                case NF_SYMBOLTYPE_FRACBLANK:
                case NF_SYMBOLTYPE_DECSEP:
                    bInInteger = false;
                    break;
                case NF_SYMBOLTYPE_THSEP:
                    if (pElemStr)
                        nTrailingThousands += pElemStr->getLength();      // is reset to 0 if digits follow
                    break;
                case NF_SYMBOLTYPE_EXP:
                    bExpFound = true;           // following digits are exponent digits
                    bInInteger = false;
                    if ( pElemStr && ( pElemStr->getLength() == 1
                                  || ( pElemStr->getLength() == 2 && (*pElemStr)[1] == '-' ) ) )
                        bExpSign = false;       // for 0.00E0 or 0.00E-00
                    if ( pElemStr && (*pElemStr)[0] == 'e' )
                        bExponentLowercase = true;   // for 0.00e+00
                    break;
                case NF_SYMBOLTYPE_CURRENCY:
                    bCurrFound = true;
                    break;
                case NF_SYMBOLTYPE_CURREXT:
                    if (pElemStr)
                        sCurrExt = *pElemStr;
                    break;

                // E, EE, R, RR: select non-gregorian calendar
                // AAA, AAAA: calendar is switched at the position of the element
                case NF_KEY_EC:
                case NF_KEY_EEC:
                case NF_KEY_R:
                case NF_KEY_RR:
                    if (aCalendar.isEmpty())
                    {
                        aCalendar = lcl_GetDefaultCalendar( m_pFormatter, nLang );
                        bImplicitOtherCalendar = true;
                    }
                    break;
            }
            ++nPos;
        }

        //  collect strings for embedded-text (must be known before number element is written)
        bool bAllowEmbedded = ( nFmtType == SvNumFormatType::ALL || nFmtType == SvNumFormatType::NUMBER ||
                                        nFmtType == SvNumFormatType::CURRENCY ||
                                        // Export only for 1.x with extensions
                                        ( nFmtType == SvNumFormatType::SCIENTIFIC && (eVersion & SvtSaveOptions::ODFSVER_EXTENDED) )||
                                        nFmtType == SvNumFormatType::PERCENT );
        if ( bAllowEmbedded )
        {
            sal_Int32 nDigitsPassed = 0;
            sal_Int32 nEmbeddedPositionsMax = nIntegerSymbols;
            // Enable embedded text in decimal part only if there's a decimal part
            if ( nPrecision )
                nEmbeddedPositionsMax += nPrecision + 1;
            // Enable embedded text in exponent in scientific number
            if ( nFmtType == SvNumFormatType::SCIENTIFIC )
                nEmbeddedPositionsMax += 1 + nExpDigits;
            nPos = 0;
            bEnd = false;
            bExpFound = false;
            while (!bEnd)
            {
                short nElemType = rFormat.GetNumForType( nPart, nPos );
                const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );

                switch ( nElemType )
                {
                    case 0:
                        bEnd = true;                // end of format reached
                        break;
                    case NF_SYMBOLTYPE_DIGIT:
                        if ( pElemStr )
                            nDigitsPassed += pElemStr->getLength();
                        break;
                    case NF_SYMBOLTYPE_EXP:
                        bExpFound = true;
                        [[fallthrough]];
                    case NF_SYMBOLTYPE_DECSEP:
                        nDigitsPassed++;
                        break;
                    case NF_SYMBOLTYPE_STRING:
                    case NF_SYMBOLTYPE_BLANK:
                    case NF_SYMBOLTYPE_PERCENT:
                        if ( 0 < nDigitsPassed && nDigitsPassed < nEmbeddedPositionsMax && pElemStr )
                        {
                            //  text (literal or underscore) within the integer (>=0) or decimal (<0) part of a number:number element

                            OUString aEmbeddedStr;
                            bool bSaveBlankWidthSymbol = false;
                            if ( nElemType == NF_SYMBOLTYPE_STRING || nElemType == NF_SYMBOLTYPE_PERCENT )
                            {
                                aEmbeddedStr = *pElemStr;
                            }
                            else if (pElemStr->getLength() >= 2)
                            {
                                if ( eVersion > SvtSaveOptions::ODFSVER_013 && ( (eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0 ) )
                                {
                                    aEmbeddedStr = pElemStr->copy( 1, 1 );
                                    bSaveBlankWidthSymbol = true;
                                }
                                else //  turn "_x" into the number of spaces used for x in InsertBlanks in the NumberFormat
                                    SvNumberformat::InsertBlanks( aEmbeddedStr, 0, (*pElemStr)[1] );
                            }
                            sal_Int32 nEmbedPos = nIntegerSymbols - nDigitsPassed;

                            aEmbeddedEntries.push_back(
                                SvXMLEmbeddedTextEntry( nPos, nEmbedPos, aEmbeddedStr, bSaveBlankWidthSymbol ));
                            // exponent sign is required with embedded text in exponent
                            if ( bExpFound && !bExpSign )
                                bExpSign = true;
                        }
                        break;
                }
                ++nPos;
            }
        }

        //  final loop to write elements

        bool bNumWritten = false;
        bool bCurrencyWritten = false;
        short nPrevType = 0;
        nPos = 0;
        bEnd = false;
        while (!bEnd)
        {
            short nElemType = rFormat.GetNumForType( nPart, nPos );
            const OUString* pElemStr = rFormat.GetNumForString( nPart, nPos );

            switch ( nElemType )
            {
                case 0:
                    bEnd = true;                // end of format reached
                    if (m_bHasText && m_sTextContent.isEmpty())
                        m_bHasText = false;       // don't write trailing empty text
                    break;
                case NF_SYMBOLTYPE_STRING:
                case NF_SYMBOLTYPE_DATESEP:
                case NF_SYMBOLTYPE_TIMESEP:
                case NF_SYMBOLTYPE_TIME100SECSEP:
                case NF_SYMBOLTYPE_PERCENT:
                    if (pElemStr)
                    {
                        if ( ( nElemType == NF_SYMBOLTYPE_TIME100SECSEP ) &&
                             ( nPrevType == NF_KEY_S || nPrevType == NF_KEY_SS ||
                               ( nPos > 0 && (*rFormat.GetNumForString( nPart, nPos-1 ))[0] == ']' &&
                               ( nFmtType == SvNumFormatType::TIME || nFmtType == SvNumFormatType::DATETIME ) ) ) &&
                             nPrecision > 0 )
                        {
                            //  decimal separator after seconds or [SS] is implied by
                            //  "decimal-places" attribute and must not be written
                            //  as text element
                            //! difference between '.' and ',' is lost here
                        }
                        else if ( lcl_IsInEmbedded( aEmbeddedEntries, nPos ) )
                        {
                            //  text is written as embedded-text child of the number,
                            //  don't create a text element
                        }
                        else if ( nFmtType == SvNumFormatType::CURRENCY && !bCurrFound && !bCurrencyWritten )
                        {
                            //  automatic currency symbol is implemented as part of
                            //  normal text -> search for the symbol
                            bCurrencyWritten = WriteTextWithCurrency_Impl( *pElemStr,
                                LanguageTag::convertToLocale( nLang ) );
                            bAnyContent = true;
                        }
                        else
                            AddToTextElement_Impl( *pElemStr );
                    }
                    break;
                case NF_SYMBOLTYPE_BLANK:
                    if ( pElemStr && !lcl_IsInEmbedded( aEmbeddedEntries, nPos ) )
                    {
                        if ( pElemStr->getLength() == 2 )
                        {
                            OUString aBlankWidthChar = pElemStr->copy( 1 );
                            lcl_WriteBlankWidthString( aBlankWidthChar, m_sBlankWidthString, m_sTextContent );
                            m_bHasText = true;
                        }
                    }
                    break;
                case NF_KEY_GENERAL :
                        WriteNumberElement_Impl( -1, -1, 1, -1, OUString(), false, 0, aEmbeddedEntries );
                        bAnyContent = true;
                    break;
                case NF_KEY_CCC:
                    if (pElemStr)
                    {
                        if ( bCurrencyWritten )
                            AddToTextElement_Impl( *pElemStr );     // never more than one currency element
                        else
                        {
                            //! must be different from short automatic format
                            //! but should still be empty (meaning automatic)
                            //  pElemStr is "CCC"

                            WriteCurrencyElement_Impl( *pElemStr, u"" );
                            bAnyContent = true;
                            bCurrencyWritten = true;
                        }
                    }
                    break;
                case NF_SYMBOLTYPE_CURRENCY:
                    if (pElemStr)
                    {
                        if ( bCurrencyWritten )
                            AddToTextElement_Impl( *pElemStr );     // never more than one currency element
                        else
                        {
                            WriteCurrencyElement_Impl( *pElemStr, sCurrExt );
                            bAnyContent = true;
                            bCurrencyWritten = true;
                        }
                    }
                    break;
                case NF_SYMBOLTYPE_DIGIT:
                    if (!bNumWritten)           // write number part
                    {
                        switch ( nFmtType )
                        {
                            // for type 0 (not recognized as a special type),
                            // write a "normal" number
                            case SvNumFormatType::ALL:
                            case SvNumFormatType::NUMBER:
                            case SvNumFormatType::CURRENCY:
                            case SvNumFormatType::PERCENT:
                                {
                                    //  decimals
                                    //  only some built-in formats have automatic decimals
                                    sal_Int32 nDecimals = nPrecision;   // from GetFormatSpecialInfo
                                    if ( eBuiltIn == NF_NUMBER_STANDARD ||
                                         eBuiltIn == NF_CURRENCY_1000DEC2 ||
                                         eBuiltIn == NF_CURRENCY_1000DEC2_RED ||
                                         eBuiltIn == NF_CURRENCY_1000DEC2_CCC ||
                                         eBuiltIn == NF_CURRENCY_1000DEC2_DASHED )
                                        nDecimals = -1;

                                    //  integer digits
                                    //  only one built-in format has automatic integer digits
                                    sal_Int32 nInteger = nLeading;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=87 H=95 G=90

¤ Dauer der Verarbeitung: 0.21 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.