diff --git a/core/script/builtin.js b/core/script/builtin.js --- a/core/script/builtin.js +++ b/core/script/builtin.js @@ -27,7 +27,7 @@ for (i = 0; i < cFields.length; i++) { var field = Doc.getField( cFields[i] ); - var val = Number( field.value ); + var val = util.stringToNumber( field.value ); if ( cFunction === "SUM" || cFunction === "AVG" ) { @@ -58,5 +58,74 @@ ret /= cFields.length; } + event.value = util.numberToString( ret, "g", 32 ); +} + + +/** AFNumber_Format + * + * Formats event.value based on parameters. + * + * Parameter description based on Acrobat Help: + * + * nDec is the number of places after the decimal point. + * + * sepStyle is an integer denoting whether to use a separator + * If it is 1 comma should be used. + * If it is 2 a dot should be used. + * The decimal seperator is changed accordingly. + * + * nexStyle is the formatting used for negative numbers: - not implemented. + * 0 = MinusBlack + * 1 = Red + * 2 = ParensBlack + * 3 = ParensRed + * + * currStyle is the currency style - not used. + * + * strCurrency is the currency symbol. + * + * bCurrencyPrepend is true to prepend the currency symbol; + * false to display on the end of the number. + */ +function AFNumber_Format( nDec, sepStyle, negStyle, currStyle, strCurrency, bCurrencyPrepend ) +{ + if ( !event.value ) + { + return; + } + + var ret; + var localized = util.stringToNumber( event.value ); + + if ( sepStyle === 2 ) + { + // Use de_DE as the locale for the dot seperator format + ret = util.numberToString( localized, "f", nDec, 'de_DE' ); + } + else + { + // Otherwise US + ret = util.numberToString( localized, "f", nDec, 'en_US' ); + } + + if ( sepStyle === 0 ) + { + // No seperators. Remove all commas from the US format. + ret.replace( /,/g, '' ); + } + + if ( strCurrency ) + { + if ( bCurrencyPrepend ) + { + ret = strCurrency + ret; + } + else + { + ret = ret + strCurrency; + } + } + event.value = ret; } diff --git a/core/script/kjs_util.cpp b/core/script/kjs_util.cpp --- a/core/script/kjs_util.cpp +++ b/core/script/kjs_util.cpp @@ -15,6 +15,9 @@ #include #include +#include + +#include using namespace Okular; @@ -56,15 +59,97 @@ return obj; } +/** Converts a Number to a String using l10n + * + * String numberToString( Number number, String format = 'g', int precision = 6, + * String LocaleName = system ) + */ +static KJSObject numberToString ( KJSContext *context, void *, + const KJSArguments &arguments ) +{ + if ( arguments.count() < 1 ) + { + return context->throwException( QStringLiteral( "Invalid arguments" ) ); + } + + double number = arguments.at( 0 ).toNumber( context ); + if ( std::isnan( number ) ) + { + return KJSString( "NaN" ); + } + + QChar format = QLatin1Char( 'g' ); + if ( arguments.count() >= 2 ) + { + const QString fmt = arguments.at( 1 ).toString( context ); + if ( fmt.size() ) + { + format = fmt[0]; + } + } + + int precision = 6; + if ( arguments.count() >= 3 ) + { + precision = arguments.at( 2 ).toInt32( context ); + } + + QLocale locale; + if ( arguments.count() == 4 ) + { + locale = QLocale( arguments.at( 3 ).toString( context ) ); + } + + // While we want to localize we don't want to loose precision + // after the default of 6 significant digits + return KJSString( locale.toString( number, format.toLatin1(), precision ) ); +} + +/** Converts a String to a Number using l10n. + * + * Number stringToNumber( String number, String LocaleName = system ) */ +static KJSObject stringToNumber ( KJSContext *context, void *, + const KJSArguments &arguments ) +{ + if ( arguments.count() < 1 ) + { + return context->throwException( QStringLiteral( "Invalid arguments" ) ); + } + + QString number = arguments.at( 0 ).toString( context ); + if ( number.isEmpty() ) + { + return KJSNumber( 0 ); + } + + QLocale locale; + if ( arguments.count() == 2 ) + { + locale = QLocale( arguments.at( 1 ).toString( context ) ); + } + + bool ok; + double converted = locale.toDouble( number, &ok ); + + if ( !ok ) + { + return KJSNumber( std::nan( "" ) ); + } + + return KJSNumber( converted ); +} + void JSUtil::initType( KJSContext *ctx ) { static bool initialized = false; if ( initialized ) return; initialized = true; g_utilProto = new KJSPrototype(); - g_utilProto->defineFunction( ctx, QStringLiteral("crackURL"), crackURL ); + g_utilProto->defineFunction( ctx, QStringLiteral( "crackURL" ), crackURL ); + g_utilProto->defineFunction( ctx, QStringLiteral( "stringToNumber" ), stringToNumber ); + g_utilProto->defineFunction( ctx, QStringLiteral( "numberToString" ), numberToString ); } KJSObject JSUtil::object( KJSContext *ctx )