forked from amazingfate/loongoffice
to split it into two constituent parts SvNFFormatData which is the data store for number formats it generally operates on. SvNFLanguageData for data around the current language in use. and then a SvNFEngine which implements the interaction between those parts SvNFEngine has two policies, the typical RW mode and a new RO mode where the SvNFFormatData doesn't change, all formats needed in this mode must already exist. Change-Id: I56b070ccd2e556a0cb1fe609a2fae28e18277c8c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165146 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
2139 lines
82 KiB
C++
2139 lines
82 KiB
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( OUString("N") ),
|
|
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.reset( new LocaleDataWrapper( m_pFormatter->GetComponentContext(),
|
|
m_pFormatter->GetLanguageTag() ) );
|
|
}
|
|
else
|
|
{
|
|
LanguageTag aLanguageTag( MsLangId::getConfiguredSystemLanguage() );
|
|
|
|
m_pLocaleData.reset( new LocaleDataWrapper( m_rExport.getComponentContext(), 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.reset( new LocaleDataWrapper( m_pFormatter->GetComponentContext(),
|
|
m_pFormatter->GetLanguageTag() ) );
|
|
}
|
|
else
|
|
{
|
|
LanguageTag aLanguageTag( MsLangId::getConfiguredSystemLanguage() );
|
|
|
|
m_pLocaleData.reset( new LocaleDataWrapper( m_rExport.getComponentContext(), 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,
|
|
true, false );
|
|
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,
|
|
true, false );
|
|
}
|
|
|
|
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,
|
|
true, false );
|
|
m_rExport.Characters( rString );
|
|
}
|
|
|
|
void SvXMLNumFmtExport::WriteBooleanElement_Impl()
|
|
{
|
|
FinishTextElement_Impl();
|
|
|
|
SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_BOOLEAN,
|
|
true, false );
|
|
}
|
|
|
|
void SvXMLNumFmtExport::WriteTextContentElement_Impl()
|
|
{
|
|
FinishTextElement_Impl();
|
|
|
|
SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_TEXT_CONTENT,
|
|
true, false );
|
|
}
|
|
|
|
// 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,
|
|
true, false );
|
|
}
|
|
|
|
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,
|
|
true, false );
|
|
}
|
|
|
|
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,
|
|
true, false );
|
|
}
|
|
|
|
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,
|
|
true, false );
|
|
}
|
|
|
|
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,
|
|
true, false );
|
|
}
|
|
|
|
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,
|
|
true, false );
|
|
}
|
|
|
|
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,
|
|
true, false );
|
|
}
|
|
|
|
// 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,
|
|
true, false );
|
|
}
|
|
|
|
void SvXMLNumFmtExport::WriteMinutesElement_Impl( bool bLong )
|
|
{
|
|
FinishTextElement_Impl();
|
|
|
|
AddStyleAttr_Impl( bLong ); // adds to pAttrList
|
|
|
|
SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_MINUTES,
|
|
true, false );
|
|
}
|
|
|
|
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, true, false );
|
|
m_rExport.Characters( OUString( nChar ) );
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
void lcl_WriteBlankWidthString( std::u16string_view rBlankWidthChar, OUStringBuffer& 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,
|
|
true, false );
|
|
}
|
|
|
|
void SvXMLNumFmtExport::WriteAMPMElement_Impl()
|
|
{
|
|
FinishTextElement_Impl();
|
|
|
|
SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_NUMBER, XML_AM_PM,
|
|
true, false );
|
|
}
|
|
|
|
// 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& 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,
|
|
true, false );
|
|
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,
|
|
true, true );
|
|
|
|
// 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,
|
|
true, false );
|
|
|
|
// 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,
|
|
true, false );
|
|
}
|
|
|
|
// 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,
|
|
true, false );
|
|
}
|
|
|
|
// 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"" );
|
|
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;
|
|
default: break;
|
|
}
|
|
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_NAMESPACE_LO_EXT /*: XML_NAMESPACE_NUMBER)*/,
|
|
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,
|
|
true, true );
|
|
|
|
// 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;
|
|
if ( eBuiltIn == NF_NUMBER_SYSTEM )
|
|
{
|
|
nInteger = -1;
|
|
nBlankInteger = -1;
|
|
}
|
|
|
|
// string for decimal replacement
|
|
// has to be taken from nPrecision
|
|
// (positive number even for automatic decimals)
|
|
OUStringBuffer sDashStr;
|
|
if (bDecDashes && nPrecision > 0)
|
|
comphelper::string::padToLength(sDashStr, nPrecision, '-');
|
|
// "?" in decimal part are replaced by space character
|
|
if (bDecAlign && nPrecision > 0)
|
|
sDashStr = " ";
|
|
|
|
WriteNumberElement_Impl(nDecimals, nMinDecimals, nInteger, nBlankInteger, sDashStr.makeStringAndClear(),
|
|
bThousand, nTrailingThousands, aEmbeddedEntries);
|
|
bAnyContent = true;
|
|
}
|
|
break;
|
|
case SvNumFormatType::SCIENTIFIC:
|
|
// #i43959# for scientific numbers, count all integer symbols ("0", "?" and "#")
|
|
// as integer digits: use nIntegerSymbols instead of nLeading
|
|
// nIntegerSymbols represents exponent interval (for engineering notation)
|
|
WriteScientificElement_Impl( nPrecision, nMinDecimals, nLeading, nBlankInteger, bThousand, nExpDigits, nIntegerSymbols, bExpSign,
|
|
bExponentLowercase, nBlankExp, aEmbeddedEntries );
|
|
bAnyContent = true;
|
|
break;
|
|
case SvNumFormatType::FRACTION:
|
|
{
|
|
sal_Int32 nInteger = nLeading;
|
|
if ( rFormat.GetNumForNumberElementCount( nPart ) == 3 )
|
|
{
|
|
// If there is only two numbers + fraction in format string
|
|
// the fraction doesn't have an integer part, and no
|
|
// min-integer-digits attribute must be written.
|
|
nInteger = -1;
|
|
nBlankInteger = -1;
|
|
}
|
|
WriteFractionElement_Impl( nInteger, nBlankInteger, bThousand, rFormat, nPart );
|
|
bAnyContent = true;
|
|
}
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
bNumWritten = true;
|
|
}
|
|
break;
|
|
case NF_SYMBOLTYPE_DECSEP:
|
|
if ( pElemStr && nPrecision == 0 )
|
|
{
|
|
// A decimal separator after the number, without following decimal digits,
|
|
// isn't modelled as part of the number element, so it's written as text
|
|
// (the distinction between a quoted and non-quoted, locale-dependent
|
|
// character is lost here).
|
|
|
|
AddToTextElement_Impl( *pElemStr );
|
|
}
|
|
break;
|
|
case NF_SYMBOLTYPE_DEL:
|
|
if ( pElemStr && *pElemStr == "@" )
|
|
{
|
|
WriteTextContentElement_Impl();
|
|
bAnyContent = true;
|
|
}
|
|
break;
|
|
|
|
case NF_SYMBOLTYPE_CALENDAR:
|
|
if ( pElemStr )
|
|
{
|
|
aCalendar = *pElemStr;
|
|
bExplicitCalendar = true;
|
|
}
|
|
break;
|
|
|
|
// date elements:
|
|
|
|
case NF_KEY_D:
|
|
case NF_KEY_DD:
|
|
{
|
|
bool bLong = ( nElemType == NF_KEY_DD );
|
|
WriteDayElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
|
|
bAnyContent = true;
|
|
}
|
|
break;
|
|
case NF_KEY_DDD:
|
|
case NF_KEY_DDDD:
|
|
case NF_KEY_NN:
|
|
case NF_KEY_NNN:
|
|
case NF_KEY_NNNN:
|
|
case NF_KEY_AAA:
|
|
case NF_KEY_AAAA:
|
|
{
|
|
OUString aCalAttr = aCalendar;
|
|
if ( nElemType == NF_KEY_AAA || nElemType == NF_KEY_AAAA )
|
|
{
|
|
// calendar attribute for AAA and AAAA is switched only for this element
|
|
if (aCalAttr.isEmpty())
|
|
aCalAttr = lcl_GetDefaultCalendar( m_pFormatter, nLang );
|
|
}
|
|
|
|
bool bLong = ( nElemType == NF_KEY_NNN || nElemType == NF_KEY_NNNN ||
|
|
nElemType == NF_KEY_DDDD || nElemType == NF_KEY_AAAA );
|
|
WriteDayOfWeekElement_Impl( aCalAttr, ( bSystemDate ? bLongSysDate : bLong ) );
|
|
bAnyContent = true;
|
|
if ( nElemType == NF_KEY_NNNN )
|
|
{
|
|
// write additional text element for separator
|
|
m_pLocaleData.reset( new LocaleDataWrapper( m_pFormatter->GetComponentContext(),
|
|
LanguageTag( nLang ) ) );
|
|
AddToTextElement_Impl( m_pLocaleData->getLongDateDayOfWeekSep() );
|
|
}
|
|
}
|
|
break;
|
|
case NF_KEY_M:
|
|
case NF_KEY_MM:
|
|
case NF_KEY_MMM:
|
|
case NF_KEY_MMMM:
|
|
case NF_KEY_MMMMM: //! first letter of month name, no attribute available
|
|
{
|
|
bool bLong = ( nElemType == NF_KEY_MM || nElemType == NF_KEY_MMMM );
|
|
bool bText = ( nElemType == NF_KEY_MMM || nElemType == NF_KEY_MMMM ||
|
|
nElemType == NF_KEY_MMMMM );
|
|
WriteMonthElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ), bText );
|
|
bAnyContent = true;
|
|
}
|
|
break;
|
|
case NF_KEY_YY:
|
|
case NF_KEY_YYYY:
|
|
case NF_KEY_EC:
|
|
case NF_KEY_EEC:
|
|
case NF_KEY_R: //! R acts as EE, no attribute available
|
|
{
|
|
//! distinguish EE and R
|
|
// Calendar attribute for E and EE and R is set in
|
|
// first loop. If set and not an explicit calendar and
|
|
// YY or YYYY is encountered, switch temporarily to
|
|
// Gregorian.
|
|
bool bLong = ( nElemType == NF_KEY_YYYY || nElemType == NF_KEY_EEC ||
|
|
nElemType == NF_KEY_R );
|
|
WriteYearElement_Impl(
|
|
((bImplicitOtherCalendar && !bExplicitCalendar
|
|
&& (nElemType == NF_KEY_YY || nElemType == NF_KEY_YYYY)) ? "gregorian" : aCalendar),
|
|
(bSystemDate ? bLongSysDate : bLong));
|
|
bAnyContent = true;
|
|
}
|
|
break;
|
|
case NF_KEY_G:
|
|
case NF_KEY_GG:
|
|
case NF_KEY_GGG:
|
|
case NF_KEY_RR: //! RR acts as GGGEE, no attribute available
|
|
{
|
|
//! distinguish GG and GGG and RR
|
|
bool bLong = ( nElemType == NF_KEY_GGG || nElemType == NF_KEY_RR );
|
|
WriteEraElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
|
|
bAnyContent = true;
|
|
if ( nElemType == NF_KEY_RR )
|
|
{
|
|
// calendar attribute for RR is set in first loop
|
|
WriteYearElement_Impl( aCalendar, ( bSystemDate || bLongSysDate ) );
|
|
}
|
|
}
|
|
break;
|
|
case NF_KEY_Q:
|
|
case NF_KEY_QQ:
|
|
{
|
|
bool bLong = ( nElemType == NF_KEY_QQ );
|
|
WriteQuarterElement_Impl( aCalendar, ( bSystemDate ? bLongSysDate : bLong ) );
|
|
bAnyContent = true;
|
|
}
|
|
break;
|
|
case NF_KEY_WW:
|
|
WriteWeekElement_Impl( aCalendar );
|
|
bAnyContent = true;
|
|
break;
|
|
|
|
// time elements (bSystemDate is not used):
|
|
|
|
case NF_KEY_H:
|
|
case NF_KEY_HH:
|
|
WriteHoursElement_Impl( nElemType == NF_KEY_HH );
|
|
bAnyContent = true;
|
|
break;
|
|
case NF_KEY_MI:
|
|
case NF_KEY_MMI:
|
|
WriteMinutesElement_Impl( nElemType == NF_KEY_MMI );
|
|
bAnyContent = true;
|
|
break;
|
|
case NF_KEY_S:
|
|
case NF_KEY_SS:
|
|
WriteSecondsElement_Impl( ( nElemType == NF_KEY_SS ), nPrecision );
|
|
bAnyContent = true;
|
|
break;
|
|
case NF_KEY_AMPM:
|
|
case NF_KEY_AP:
|
|
WriteAMPMElement_Impl(); // short/long?
|
|
bAnyContent = true;
|
|
break;
|
|
case NF_SYMBOLTYPE_STAR :
|
|
// export only if ODF 1.2 extensions are enabled
|
|
if (m_rExport.getSaneDefaultVersion() > SvtSaveOptions::ODFSVER_012)
|
|
{
|
|
if ( pElemStr && pElemStr->getLength() > 1 )
|
|
WriteRepeatedElement_Impl( (*pElemStr)[1] );
|
|
}
|
|
break;
|
|
}
|
|
nPrevType = nElemType;
|
|
++nPos;
|
|
}
|
|
}
|
|
|
|
if ( !m_sTextContent.isEmpty() )
|
|
bAnyContent = true; // element written in FinishTextElement_Impl
|
|
|
|
FinishTextElement_Impl(); // final text element - before maps
|
|
|
|
if ( !bAnyContent )
|
|
{
|
|
// for an empty format, write an empty text element
|
|
SvXMLElementExport aTElem( m_rExport, XML_NAMESPACE_NUMBER, XML_TEXT,
|
|
true, false );
|
|
}
|
|
|
|
// mapping (conditions) must be last elements
|
|
|
|
if (!bDefPart)
|
|
return;
|
|
|
|
SvNumberformatLimitOps eOp1, eOp2;
|
|
double fLimit1, fLimit2;
|
|
rFormat.GetConditions( eOp1, fLimit1, eOp2, fLimit2 );
|
|
|
|
WriteMapElement_Impl( eOp1, fLimit1, nKey, 0 );
|
|
WriteMapElement_Impl( eOp2, fLimit2, nKey, 1 );
|
|
|
|
if ( !rFormat.HasTextFormat() )
|
|
return;
|
|
|
|
// 4th part is for text -> make an "all other numbers" condition for the 3rd part
|
|
// by reversing the 2nd condition.
|
|
// For a trailing text format like 0;@ that has no conditions
|
|
// use a "less or equal than biggest" condition for the number
|
|
// part, ODF can't store subformats (style maps) without
|
|
// conditions.
|
|
|
|
SvNumberformatLimitOps eOp3 = NUMBERFORMAT_OP_NO;
|
|
double fLimit3 = fLimit2;
|
|
sal_uInt16 nLastPart = 2;
|
|
SvNumberformatLimitOps eOpLast = eOp2;
|
|
if (eOp2 == NUMBERFORMAT_OP_NO)
|
|
{
|
|
eOpLast = eOp1;
|
|
fLimit3 = fLimit1;
|
|
nLastPart = (eOp1 == NUMBERFORMAT_OP_NO) ? 0 : 1;
|
|
}
|
|
switch ( eOpLast )
|
|
{
|
|
case NUMBERFORMAT_OP_EQ: eOp3 = NUMBERFORMAT_OP_NE; break;
|
|
case NUMBERFORMAT_OP_NE: eOp3 = NUMBERFORMAT_OP_EQ; break;
|
|
case NUMBERFORMAT_OP_LT: eOp3 = NUMBERFORMAT_OP_GE; break;
|
|
case NUMBERFORMAT_OP_LE: eOp3 = NUMBERFORMAT_OP_GT; break;
|
|
case NUMBERFORMAT_OP_GT: eOp3 = NUMBERFORMAT_OP_LE; break;
|
|
case NUMBERFORMAT_OP_GE: eOp3 = NUMBERFORMAT_OP_LT; break;
|
|
case NUMBERFORMAT_OP_NO: eOp3 = NUMBERFORMAT_OP_LE; fLimit3 = DBL_MAX; break;
|
|
}
|
|
|
|
if ( fLimit1 == fLimit2 &&
|
|
( ( eOp1 == NUMBERFORMAT_OP_LT && eOp2 == NUMBERFORMAT_OP_GT ) ||
|
|
( eOp1 == NUMBERFORMAT_OP_GT && eOp2 == NUMBERFORMAT_OP_LT ) ) )
|
|
{
|
|
// For <x and >x, add =x as last condition
|
|
// (just for readability, <=x would be valid, too)
|
|
|
|
eOp3 = NUMBERFORMAT_OP_EQ;
|
|
}
|
|
|
|
WriteMapElement_Impl( eOp3, fLimit3, nKey, nLastPart );
|
|
}
|
|
|
|
// export one format
|
|
|
|
void SvXMLNumFmtExport::ExportFormat_Impl( const SvNumberformat& rFormat, sal_uInt32 nKey, sal_uInt32 nRealKey )
|
|
{
|
|
const sal_uInt16 XMLNUM_MAX_PARTS = 4;
|
|
bool bParts[XMLNUM_MAX_PARTS] = { false, false, false, false };
|
|
sal_uInt16 nUsedParts = 0;
|
|
for (sal_uInt16 nPart=0; nPart<XMLNUM_MAX_PARTS; ++nPart)
|
|
{
|
|
if (rFormat.GetNumForInfoScannedType( nPart) != SvNumFormatType::UNDEFINED)
|
|
{
|
|
bParts[nPart] = true;
|
|
nUsedParts = nPart + 1;
|
|
}
|
|
}
|
|
|
|
SvNumberformatLimitOps eOp1, eOp2;
|
|
double fLimit1, fLimit2;
|
|
rFormat.GetConditions( eOp1, fLimit1, eOp2, fLimit2 );
|
|
|
|
// if conditions are set, even empty formats must be written
|
|
|
|
if ( eOp1 != NUMBERFORMAT_OP_NO )
|
|
{
|
|
bParts[1] = true;
|
|
if (nUsedParts < 2)
|
|
nUsedParts = 2;
|
|
}
|
|
if ( eOp2 != NUMBERFORMAT_OP_NO )
|
|
{
|
|
bParts[2] = true;
|
|
if (nUsedParts < 3)
|
|
nUsedParts = 3;
|
|
}
|
|
if ( rFormat.HasTextFormat() )
|
|
{
|
|
bParts[3] = true;
|
|
if (nUsedParts < 4)
|
|
nUsedParts = 4;
|
|
}
|
|
|
|
for (sal_uInt16 nPart=0; nPart<XMLNUM_MAX_PARTS; ++nPart)
|
|
{
|
|
if (bParts[nPart])
|
|
{
|
|
bool bDefault = ( nPart+1 == nUsedParts ); // last = default
|
|
ExportPart_Impl( rFormat, nKey, nRealKey, nPart, bDefault );
|
|
}
|
|
}
|
|
}
|
|
|
|
// export method called by application
|
|
|
|
void SvXMLNumFmtExport::Export( bool bIsAutoStyle )
|
|
{
|
|
if ( !m_pFormatter )
|
|
return; // no formatter -> no entries
|
|
|
|
sal_uInt32 nKey;
|
|
const SvNumberformat* pFormat = nullptr;
|
|
bool bNext(m_pUsedList->GetFirstUsed(nKey));
|
|
while(bNext)
|
|
{
|
|
// ODF has its notation of system formats, so obtain the "real" already
|
|
// substituted format but use the original key for style name.
|
|
sal_uInt32 nRealKey = nKey;
|
|
pFormat = m_pFormatter->GetSubstitutedEntry( nKey, nRealKey);
|
|
if(pFormat)
|
|
ExportFormat_Impl( *pFormat, nKey, nRealKey );
|
|
bNext = m_pUsedList->GetNextUsed(nKey);
|
|
}
|
|
if (!bIsAutoStyle)
|
|
{
|
|
std::vector<LanguageType> aLanguages;
|
|
m_pFormatter->GetUsedLanguages( aLanguages );
|
|
for (const auto& nLang : aLanguages)
|
|
{
|
|
sal_uInt32 nDefaultIndex = 0;
|
|
SvNumberFormatTable& rTable = m_pFormatter->GetEntryTable(
|
|
SvNumFormatType::DEFINED, nDefaultIndex, nLang );
|
|
for (const auto& rTableEntry : rTable)
|
|
{
|
|
nKey = rTableEntry.first;
|
|
pFormat = rTableEntry.second;
|
|
if (!m_pUsedList->IsUsed(nKey))
|
|
{
|
|
DBG_ASSERT((pFormat->GetType() & SvNumFormatType::DEFINED), "a not user defined numberformat found");
|
|
sal_uInt32 nRealKey = nKey;
|
|
if (pFormat->IsSubstituted())
|
|
{
|
|
pFormat = m_pFormatter->GetSubstitutedEntry( nKey, nRealKey); // export the "real" format
|
|
assert(pFormat);
|
|
}
|
|
// user-defined and used formats are exported
|
|
ExportFormat_Impl( *pFormat, nKey, nRealKey );
|
|
// if it is a user-defined Format it will be added else nothing will happen
|
|
m_pUsedList->SetUsed(nKey);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
m_pUsedList->Export();
|
|
}
|
|
|
|
OUString SvXMLNumFmtExport::GetStyleName( sal_uInt32 nKey )
|
|
{
|
|
if(m_pUsedList->IsUsed(nKey) || m_pUsedList->IsWasUsed(nKey))
|
|
return lcl_CreateStyleName( nKey, 0, true, m_sPrefix );
|
|
else
|
|
{
|
|
OSL_FAIL("There is no written Data-Style");
|
|
return OUString();
|
|
}
|
|
}
|
|
|
|
void SvXMLNumFmtExport::SetUsed( sal_uInt32 nKey )
|
|
{
|
|
SAL_WARN_IF( m_pFormatter == nullptr, "xmloff.style", "missing formatter" );
|
|
if( !m_pFormatter )
|
|
return;
|
|
|
|
if (m_pFormatter->GetEntry(nKey))
|
|
m_pUsedList->SetUsed( nKey );
|
|
else {
|
|
OSL_FAIL("no existing Numberformat found with this key");
|
|
}
|
|
}
|
|
|
|
uno::Sequence<sal_Int32> SvXMLNumFmtExport::GetWasUsed() const
|
|
{
|
|
if (m_pUsedList)
|
|
return m_pUsedList->GetWasUsed();
|
|
return uno::Sequence<sal_Int32>();
|
|
}
|
|
|
|
void SvXMLNumFmtExport::SetWasUsed(const uno::Sequence<sal_Int32>& rWasUsed)
|
|
{
|
|
if (m_pUsedList)
|
|
m_pUsedList->SetWasUsed(rWasUsed);
|
|
}
|
|
|
|
static const SvNumberformat* lcl_GetFormat( SvNumberFormatter const * pFormatter,
|
|
sal_uInt32 nKey )
|
|
{
|
|
return ( pFormatter != nullptr ) ? pFormatter->GetEntry( nKey ) : nullptr;
|
|
}
|
|
|
|
sal_uInt32 SvXMLNumFmtExport::ForceSystemLanguage( sal_uInt32 nKey )
|
|
{
|
|
sal_uInt32 nRet = nKey;
|
|
|
|
const SvNumberformat* pFormat = lcl_GetFormat( m_pFormatter, nKey );
|
|
if( pFormat != nullptr )
|
|
{
|
|
SAL_WARN_IF( m_pFormatter == nullptr, "xmloff.style", "format without formatter?" );
|
|
|
|
SvNumFormatType nType = pFormat->GetType();
|
|
|
|
sal_uInt32 nNewKey = m_pFormatter->GetFormatForLanguageIfBuiltIn(
|
|
nKey, LANGUAGE_SYSTEM );
|
|
|
|
if( nNewKey != nKey )
|
|
{
|
|
nRet = nNewKey;
|
|
}
|
|
else
|
|
{
|
|
OUString aFormatString( pFormat->GetFormatstring() );
|
|
sal_Int32 nErrorPos;
|
|
m_pFormatter->PutandConvertEntry(
|
|
aFormatString,
|
|
nErrorPos, nType, nNewKey,
|
|
pFormat->GetLanguage(), LANGUAGE_SYSTEM, true);
|
|
|
|
// success? Then use new key.
|
|
if( nErrorPos == 0 )
|
|
nRet = nNewKey;
|
|
}
|
|
}
|
|
|
|
return nRet;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|