/* -*- 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 .
*/
// 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;
}
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
}
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 );
}
}
}
// 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() );
}
}
// 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 );
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 ) );
}
}
// 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 ) );
}
}
// number:embedded-text as child elements // Export only for 1.x with extensions if (eVersion & SvtSaveOptions::ODFSVER_EXTENDED)
WriteEmbeddedEntries_Impl( rEmbeddedEntries );
}
// 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
// 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
staticbool 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 ) returntrue;
returnfalse; // not found
}
staticbool 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
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;
}
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!
// 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; elseif ( eBuiltIn == NF_BOOLEAN )
nFmtType = SvNumFormatType::LOGICAL; elseif ( 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" );
// 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 );
}
// 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
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
// 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
// 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, 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;
} elseif ( eBuiltIn == NF_BOOLEAN )
{ // boolean format contains just one boolean element
WriteBooleanElement_Impl();
bAnyContent = true;
} elseif (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)
{ constshort 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 ++;
}
} elseif ( !bDecDashes && pElemStr && (*pElemStr)[0] == '-' )
{
bDecDashes = true;
nMinDecimals = 0;
} elseif ( 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;
} elseif (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
} elseif ( lcl_IsInEmbedded( aEmbeddedEntries, nPos ) )
{ // text is written as embedded-text child of the number, // don't create a text element
} elseif ( 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
¤ Dauer der Verarbeitung: 0.36 Sekunden
(vorverarbeitet)
¤
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.