1*cdf0e10cSrcweir /************************************************************************* 2*cdf0e10cSrcweir * 3*cdf0e10cSrcweir * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4*cdf0e10cSrcweir * 5*cdf0e10cSrcweir * Copyright 2000, 2010 Oracle and/or its affiliates. 6*cdf0e10cSrcweir * 7*cdf0e10cSrcweir * OpenOffice.org - a multi-platform office productivity suite 8*cdf0e10cSrcweir * 9*cdf0e10cSrcweir * This file is part of OpenOffice.org. 10*cdf0e10cSrcweir * 11*cdf0e10cSrcweir * OpenOffice.org is free software: you can redistribute it and/or modify 12*cdf0e10cSrcweir * it under the terms of the GNU Lesser General Public License version 3 13*cdf0e10cSrcweir * only, as published by the Free Software Foundation. 14*cdf0e10cSrcweir * 15*cdf0e10cSrcweir * OpenOffice.org is distributed in the hope that it will be useful, 16*cdf0e10cSrcweir * but WITHOUT ANY WARRANTY; without even the implied warranty of 17*cdf0e10cSrcweir * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18*cdf0e10cSrcweir * GNU Lesser General Public License version 3 for more details 19*cdf0e10cSrcweir * (a copy is included in the LICENSE file that accompanied this code). 20*cdf0e10cSrcweir * 21*cdf0e10cSrcweir * You should have received a copy of the GNU Lesser General Public License 22*cdf0e10cSrcweir * version 3 along with OpenOffice.org. If not, see 23*cdf0e10cSrcweir * <http://www.openoffice.org/license.html> 24*cdf0e10cSrcweir * for a copy of the LGPLv3 License. 25*cdf0e10cSrcweir * 26*cdf0e10cSrcweir ************************************************************************/ 27*cdf0e10cSrcweir 28*cdf0e10cSrcweir #include "oox/xls/unitconverter.hxx" 29*cdf0e10cSrcweir 30*cdf0e10cSrcweir #include <com/sun/star/awt/DeviceInfo.hpp> 31*cdf0e10cSrcweir #include <com/sun/star/awt/FontDescriptor.hpp> 32*cdf0e10cSrcweir #include <com/sun/star/awt/XDevice.hpp> 33*cdf0e10cSrcweir #include <com/sun/star/awt/XFont.hpp> 34*cdf0e10cSrcweir #include <com/sun/star/util/Date.hpp> 35*cdf0e10cSrcweir #include <com/sun/star/util/DateTime.hpp> 36*cdf0e10cSrcweir #include <rtl/math.hxx> 37*cdf0e10cSrcweir #include "oox/core/filterbase.hxx" 38*cdf0e10cSrcweir #include "oox/helper/propertyset.hxx" 39*cdf0e10cSrcweir #include "oox/xls/stylesbuffer.hxx" 40*cdf0e10cSrcweir 41*cdf0e10cSrcweir namespace oox { 42*cdf0e10cSrcweir namespace xls { 43*cdf0e10cSrcweir 44*cdf0e10cSrcweir // ============================================================================ 45*cdf0e10cSrcweir 46*cdf0e10cSrcweir using namespace ::com::sun::star::awt; 47*cdf0e10cSrcweir using namespace ::com::sun::star::uno; 48*cdf0e10cSrcweir using namespace ::com::sun::star::util; 49*cdf0e10cSrcweir 50*cdf0e10cSrcweir using ::rtl::OUString; 51*cdf0e10cSrcweir 52*cdf0e10cSrcweir // ============================================================================ 53*cdf0e10cSrcweir 54*cdf0e10cSrcweir namespace { 55*cdf0e10cSrcweir 56*cdf0e10cSrcweir const double MM100_PER_INCH = 2540.0; 57*cdf0e10cSrcweir const double MM100_PER_POINT = MM100_PER_INCH / 72.0; 58*cdf0e10cSrcweir const double MM100_PER_TWIP = MM100_PER_POINT / 20.0; 59*cdf0e10cSrcweir const double MM100_PER_EMU = 1.0 / 360.0; 60*cdf0e10cSrcweir 61*cdf0e10cSrcweir // ---------------------------------------------------------------------------- 62*cdf0e10cSrcweir 63*cdf0e10cSrcweir /** Returns true, if the passed year is a leap year. */ 64*cdf0e10cSrcweir inline sal_Int32 lclIsLeapYear( sal_Int32 nYear ) 65*cdf0e10cSrcweir { 66*cdf0e10cSrcweir return ((nYear % 4) == 0) && (((nYear % 100) != 0) || ((nYear % 400) == 0)); 67*cdf0e10cSrcweir } 68*cdf0e10cSrcweir 69*cdf0e10cSrcweir void lclSkipYearBlock( sal_Int32& ornDays, sal_uInt16& ornYear, sal_Int32 nDaysInBlock, sal_Int32 nYearsPerBlock, sal_Int32 nMaxBlocks ) 70*cdf0e10cSrcweir { 71*cdf0e10cSrcweir sal_Int32 nBlocks = ::std::min< sal_Int32 >( ornDays / nDaysInBlock, nMaxBlocks ); 72*cdf0e10cSrcweir ornYear = static_cast< sal_uInt16 >( ornYear + nYearsPerBlock * nBlocks ); 73*cdf0e10cSrcweir ornDays -= nBlocks * nDaysInBlock; 74*cdf0e10cSrcweir } 75*cdf0e10cSrcweir 76*cdf0e10cSrcweir /** Returns the number of days before the passed date, starting from the null 77*cdf0e10cSrcweir date 0000-Jan-01, using standard leap year conventions. */ 78*cdf0e10cSrcweir sal_Int32 lclGetDays( const Date& rDate ) 79*cdf0e10cSrcweir { 80*cdf0e10cSrcweir // number of days in all full years before passed date including all leap days 81*cdf0e10cSrcweir sal_Int32 nDays = rDate.Year * 365 + ((rDate.Year + 3) / 4) - ((rDate.Year + 99) / 100) + ((rDate.Year + 399) / 400); 82*cdf0e10cSrcweir OSL_ENSURE( (1 <= rDate.Month) && (rDate.Month <= 12), "lclGetDays - invalid month" ); 83*cdf0e10cSrcweir OSL_ENSURE( (1 <= rDate.Day) && (rDate.Day <= 31), "lclGetDays - invalid day" ); // yes, this is weak... 84*cdf0e10cSrcweir if( (1 <= rDate.Month) && (rDate.Month <= 12) ) 85*cdf0e10cSrcweir { 86*cdf0e10cSrcweir // number of days at start of month jan feb mar apr may jun jul aug sep oct nov dec 87*cdf0e10cSrcweir static const sal_Int32 spnCumDays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; 88*cdf0e10cSrcweir // add number of days in full months before passed date 89*cdf0e10cSrcweir nDays += spnCumDays[ rDate.Month - 1 ]; 90*cdf0e10cSrcweir // add number of days from passed date (this adds one day too much) 91*cdf0e10cSrcweir nDays += rDate.Day; 92*cdf0e10cSrcweir /* Remove the one day added too much if there is no leap day before 93*cdf0e10cSrcweir the passed day in the passed year. This means: remove the day, if 94*cdf0e10cSrcweir we are in january or february (leap day not reached if existing), 95*cdf0e10cSrcweir or if the passed year is not a leap year. */ 96*cdf0e10cSrcweir if( (rDate.Month < 3) || !lclIsLeapYear( rDate.Year ) ) 97*cdf0e10cSrcweir --nDays; 98*cdf0e10cSrcweir } 99*cdf0e10cSrcweir return nDays; 100*cdf0e10cSrcweir } 101*cdf0e10cSrcweir 102*cdf0e10cSrcweir } // namespace 103*cdf0e10cSrcweir 104*cdf0e10cSrcweir // ---------------------------------------------------------------------------- 105*cdf0e10cSrcweir 106*cdf0e10cSrcweir UnitConverter::UnitConverter( const WorkbookHelper& rHelper ) : 107*cdf0e10cSrcweir WorkbookHelper( rHelper ), 108*cdf0e10cSrcweir maCoeffs( UNIT_ENUM_SIZE, 1.0 ), 109*cdf0e10cSrcweir mnNullDate( lclGetDays( Date( 30, 12, 1899 ) ) ) 110*cdf0e10cSrcweir { 111*cdf0e10cSrcweir // initialize constant and default coefficients 112*cdf0e10cSrcweir const DeviceInfo& rDeviceInfo = getBaseFilter().getGraphicHelper().getDeviceInfo(); 113*cdf0e10cSrcweir maCoeffs[ UNIT_INCH ] = MM100_PER_INCH; 114*cdf0e10cSrcweir maCoeffs[ UNIT_POINT ] = MM100_PER_POINT; 115*cdf0e10cSrcweir maCoeffs[ UNIT_TWIP ] = MM100_PER_TWIP; 116*cdf0e10cSrcweir maCoeffs[ UNIT_EMU ] = MM100_PER_EMU; 117*cdf0e10cSrcweir maCoeffs[ UNIT_SCREENX ] = (rDeviceInfo.PixelPerMeterX > 0) ? (100000.0 / rDeviceInfo.PixelPerMeterX) : 50.0; 118*cdf0e10cSrcweir maCoeffs[ UNIT_SCREENY ] = (rDeviceInfo.PixelPerMeterY > 0) ? (100000.0 / rDeviceInfo.PixelPerMeterY) : 50.0; 119*cdf0e10cSrcweir maCoeffs[ UNIT_REFDEVX ] = 12.5; // default: 1 px = 0.125 mm 120*cdf0e10cSrcweir maCoeffs[ UNIT_REFDEVY ] = 12.5; // default: 1 px = 0.125 mm 121*cdf0e10cSrcweir maCoeffs[ UNIT_DIGIT ] = 200.0; // default: 1 digit = 2 mm 122*cdf0e10cSrcweir maCoeffs[ UNIT_SPACE ] = 100.0; // default 1 space = 1 mm 123*cdf0e10cSrcweir 124*cdf0e10cSrcweir // error code maps 125*cdf0e10cSrcweir addErrorCode( BIFF_ERR_NULL, CREATE_OUSTRING( "#NULL!" ) ); 126*cdf0e10cSrcweir addErrorCode( BIFF_ERR_DIV0, CREATE_OUSTRING( "#DIV/0!" ) ); 127*cdf0e10cSrcweir addErrorCode( BIFF_ERR_VALUE, CREATE_OUSTRING( "#VALUE!" ) ); 128*cdf0e10cSrcweir addErrorCode( BIFF_ERR_REF, CREATE_OUSTRING( "#REF!" ) ); 129*cdf0e10cSrcweir addErrorCode( BIFF_ERR_NAME, CREATE_OUSTRING( "#NAME?" ) ); 130*cdf0e10cSrcweir addErrorCode( BIFF_ERR_NUM, CREATE_OUSTRING( "#NUM!" ) ); 131*cdf0e10cSrcweir addErrorCode( BIFF_ERR_NA, CREATE_OUSTRING( "#NA" ) ); 132*cdf0e10cSrcweir } 133*cdf0e10cSrcweir 134*cdf0e10cSrcweir void UnitConverter::finalizeImport() 135*cdf0e10cSrcweir { 136*cdf0e10cSrcweir PropertySet aDocProps( getDocument() ); 137*cdf0e10cSrcweir Reference< XDevice > xDevice( aDocProps.getAnyProperty( PROP_ReferenceDevice ), UNO_QUERY ); 138*cdf0e10cSrcweir if( xDevice.is() ) 139*cdf0e10cSrcweir { 140*cdf0e10cSrcweir // get reference device metric first, needed to get character widths below 141*cdf0e10cSrcweir DeviceInfo aInfo = xDevice->getInfo(); 142*cdf0e10cSrcweir maCoeffs[ UNIT_REFDEVX ] = 100000.0 / aInfo.PixelPerMeterX; 143*cdf0e10cSrcweir maCoeffs[ UNIT_REFDEVY ] = 100000.0 / aInfo.PixelPerMeterY; 144*cdf0e10cSrcweir 145*cdf0e10cSrcweir // get character widths from default font 146*cdf0e10cSrcweir if( const Font* pDefFont = getStyles().getDefaultFont().get() ) 147*cdf0e10cSrcweir { 148*cdf0e10cSrcweir // XDevice expects pixels in font descriptor, but font contains twips 149*cdf0e10cSrcweir FontDescriptor aDesc = pDefFont->getFontDescriptor(); 150*cdf0e10cSrcweir aDesc.Height = static_cast< sal_Int16 >( scaleValue( aDesc.Height, UNIT_TWIP, UNIT_REFDEVX ) + 0.5 ); 151*cdf0e10cSrcweir Reference< XFont > xFont = xDevice->getFont( aDesc ); 152*cdf0e10cSrcweir if( xFont.is() ) 153*cdf0e10cSrcweir { 154*cdf0e10cSrcweir // get maximum width of all digits 155*cdf0e10cSrcweir sal_Int32 nDigitWidth = 0; 156*cdf0e10cSrcweir for( sal_Unicode cChar = '0'; cChar <= '9'; ++cChar ) 157*cdf0e10cSrcweir nDigitWidth = ::std::max( nDigitWidth, scaleToMm100( xFont->getCharWidth( cChar ), UNIT_REFDEVX ) ); 158*cdf0e10cSrcweir if( nDigitWidth > 0 ) 159*cdf0e10cSrcweir maCoeffs[ UNIT_DIGIT ] = nDigitWidth; 160*cdf0e10cSrcweir // get width of space character 161*cdf0e10cSrcweir sal_Int32 nSpaceWidth = scaleToMm100( xFont->getCharWidth( ' ' ), UNIT_REFDEVX ); 162*cdf0e10cSrcweir if( nSpaceWidth > 0 ) 163*cdf0e10cSrcweir maCoeffs[ UNIT_SPACE ] = nSpaceWidth; 164*cdf0e10cSrcweir } 165*cdf0e10cSrcweir } 166*cdf0e10cSrcweir } 167*cdf0e10cSrcweir } 168*cdf0e10cSrcweir 169*cdf0e10cSrcweir void UnitConverter::finalizeNullDate( const Date& rNullDate ) 170*cdf0e10cSrcweir { 171*cdf0e10cSrcweir // convert the nulldate to number of days since 0000-Jan-01 172*cdf0e10cSrcweir mnNullDate = lclGetDays( rNullDate ); 173*cdf0e10cSrcweir } 174*cdf0e10cSrcweir 175*cdf0e10cSrcweir // conversion ----------------------------------------------------------------- 176*cdf0e10cSrcweir 177*cdf0e10cSrcweir double UnitConverter::scaleValue( double fValue, Unit eFromUnit, Unit eToUnit ) const 178*cdf0e10cSrcweir { 179*cdf0e10cSrcweir return (eFromUnit == eToUnit) ? fValue : (fValue * getCoefficient( eFromUnit ) / getCoefficient( eToUnit )); 180*cdf0e10cSrcweir } 181*cdf0e10cSrcweir 182*cdf0e10cSrcweir sal_Int32 UnitConverter::scaleToMm100( double fValue, Unit eUnit ) const 183*cdf0e10cSrcweir { 184*cdf0e10cSrcweir return static_cast< sal_Int32 >( fValue * getCoefficient( eUnit ) + 0.5 ); 185*cdf0e10cSrcweir } 186*cdf0e10cSrcweir 187*cdf0e10cSrcweir double UnitConverter::scaleFromMm100( sal_Int32 nMm100, Unit eUnit ) const 188*cdf0e10cSrcweir { 189*cdf0e10cSrcweir return static_cast< double >( nMm100 ) / getCoefficient( eUnit ); 190*cdf0e10cSrcweir } 191*cdf0e10cSrcweir 192*cdf0e10cSrcweir double UnitConverter::calcSerialFromDateTime( const DateTime& rDateTime ) const 193*cdf0e10cSrcweir { 194*cdf0e10cSrcweir sal_Int32 nDays = lclGetDays( Date( rDateTime.Day, rDateTime.Month, rDateTime.Year ) ) - mnNullDate; 195*cdf0e10cSrcweir OSL_ENSURE( nDays >= 0, "UnitConverter::calcDateTimeSerial - invalid date" ); 196*cdf0e10cSrcweir OSL_ENSURE( (rDateTime.Hours <= 23) && (rDateTime.Minutes <= 59) && (rDateTime.Seconds <= 59), "UnitConverter::calcDateTimeSerial - invalid time" ); 197*cdf0e10cSrcweir return nDays + rDateTime.Hours / 24.0 + rDateTime.Minutes / 1440.0 + rDateTime.Seconds / 86400.0; 198*cdf0e10cSrcweir } 199*cdf0e10cSrcweir 200*cdf0e10cSrcweir DateTime UnitConverter::calcDateTimeFromSerial( double fSerial ) const 201*cdf0e10cSrcweir { 202*cdf0e10cSrcweir DateTime aDateTime( 0, 0, 0, 0, 1, 1, 0 ); 203*cdf0e10cSrcweir double fDays = 0.0; 204*cdf0e10cSrcweir double fTime = modf( fSerial, &fDays ); 205*cdf0e10cSrcweir 206*cdf0e10cSrcweir // calculate date from number of days with O(1) complexity 207*cdf0e10cSrcweir sal_Int32 nDays = getLimitedValue< sal_Int32, double >( fDays + mnNullDate, 0, 3652424 ); 208*cdf0e10cSrcweir // skip year 0, assumed to be a leap year. By starting at year 1, leap years can be handled easily 209*cdf0e10cSrcweir if( nDays >= 366 ) { ++aDateTime.Year; nDays -= 366; } 210*cdf0e10cSrcweir // skip full blocks of 400, 100, 4 years, and remaining full years 211*cdf0e10cSrcweir lclSkipYearBlock( nDays, aDateTime.Year, 400 * 365 + 97, 400, 24 ); 212*cdf0e10cSrcweir lclSkipYearBlock( nDays, aDateTime.Year, 100 * 365 + 24, 100, 3 ); 213*cdf0e10cSrcweir lclSkipYearBlock( nDays, aDateTime.Year, 4 * 365 + 1, 4, 24 ); 214*cdf0e10cSrcweir lclSkipYearBlock( nDays, aDateTime.Year, 365, 1, 3 ); 215*cdf0e10cSrcweir // skip full months of current year 216*cdf0e10cSrcweir static const sal_Int32 spnDaysInMonth[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 217*cdf0e10cSrcweir if( (nDays >= 59) && !lclIsLeapYear( aDateTime.Year ) ) ++nDays; 218*cdf0e10cSrcweir const sal_Int32* pnDaysInMonth = spnDaysInMonth; 219*cdf0e10cSrcweir while( *pnDaysInMonth >= nDays ) { ++aDateTime.Month; nDays -= *pnDaysInMonth; ++pnDaysInMonth; } 220*cdf0e10cSrcweir aDateTime.Day = static_cast< sal_uInt16 >( nDays + 1 ); 221*cdf0e10cSrcweir 222*cdf0e10cSrcweir // calculate time from fractional part of serial 223*cdf0e10cSrcweir sal_Int32 nTime = getLimitedValue< sal_Int32, double >( fTime * 86400, 0, 86399 ); 224*cdf0e10cSrcweir aDateTime.Seconds = static_cast< sal_uInt16 >( nTime % 60 ); 225*cdf0e10cSrcweir nTime /= 60; 226*cdf0e10cSrcweir aDateTime.Minutes = static_cast< sal_uInt16 >( nTime % 60 ); 227*cdf0e10cSrcweir aDateTime.Hours = static_cast< sal_uInt16 >( nTime / 60 ); 228*cdf0e10cSrcweir 229*cdf0e10cSrcweir return aDateTime; 230*cdf0e10cSrcweir } 231*cdf0e10cSrcweir 232*cdf0e10cSrcweir OUString UnitConverter::calcOoxErrorCode( sal_uInt8 nErrorCode ) const 233*cdf0e10cSrcweir { 234*cdf0e10cSrcweir BiffErrorCodeMap::const_iterator aIt = maBiffErrCodes.find( nErrorCode ); 235*cdf0e10cSrcweir return (aIt == maBiffErrCodes.end()) ? CREATE_OUSTRING( "#N/A" ) : aIt->second; 236*cdf0e10cSrcweir } 237*cdf0e10cSrcweir 238*cdf0e10cSrcweir sal_uInt8 UnitConverter::calcBiffErrorCode( const OUString& rErrorCode ) const 239*cdf0e10cSrcweir { 240*cdf0e10cSrcweir OoxErrorCodeMap::const_iterator aIt = maOoxErrCodes.find( rErrorCode ); 241*cdf0e10cSrcweir return (aIt == maOoxErrCodes.end()) ? BIFF_ERR_NA : aIt->second; 242*cdf0e10cSrcweir } 243*cdf0e10cSrcweir 244*cdf0e10cSrcweir void UnitConverter::addErrorCode( sal_uInt8 nErrorCode, const OUString& rErrorCode ) 245*cdf0e10cSrcweir { 246*cdf0e10cSrcweir maOoxErrCodes[ rErrorCode ] = nErrorCode; 247*cdf0e10cSrcweir maBiffErrCodes[ nErrorCode ] = rErrorCode; 248*cdf0e10cSrcweir } 249*cdf0e10cSrcweir 250*cdf0e10cSrcweir double UnitConverter::getCoefficient( Unit eUnit ) const 251*cdf0e10cSrcweir { 252*cdf0e10cSrcweir OSL_ENSURE( static_cast< size_t >( eUnit ) < UNIT_ENUM_SIZE, "UnitConverter::getCoefficient - invalid unit" ); 253*cdf0e10cSrcweir return maCoeffs[ static_cast< size_t >( eUnit ) ]; 254*cdf0e10cSrcweir } 255*cdf0e10cSrcweir 256*cdf0e10cSrcweir // ============================================================================ 257*cdf0e10cSrcweir 258*cdf0e10cSrcweir } // namespace xls 259*cdf0e10cSrcweir } // namespace oox 260