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