LibreOffice Module sc (master) 1
unitconverter.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <unitconverter.hxx>
21
22#include <com/sun/star/awt/DeviceInfo.hpp>
23#include <com/sun/star/awt/XDevice.hpp>
24#include <com/sun/star/awt/XFont.hpp>
25#include <com/sun/star/beans/XPropertySet.hpp>
26#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
27#include <com/sun/star/util/Date.hpp>
28#include <com/sun/star/util/DateTime.hpp>
30#include <osl/diagnose.h>
33#include <oox/token/properties.hxx>
34#include <stylesbuffer.hxx>
35#include <biffhelper.hxx>
36
37namespace com::sun::star::awt { struct FontDescriptor; }
38
39namespace oox::xls {
40
41using namespace ::com::sun::star;
42using namespace ::com::sun::star::awt;
43using namespace ::com::sun::star::uno;
44
45namespace {
46
48bool lclIsLeapYear( sal_Int32 nYear )
49{
50 return ((nYear % 4) == 0) && (((nYear % 100) != 0) || ((nYear % 400) == 0));
51}
52
53void lclSkipYearBlock( sal_Int32& ornDays, sal_Int16& ornYear, sal_Int32 nDaysInBlock, sal_Int32 nYearsPerBlock, sal_Int32 nMaxBlocks )
54{
55 sal_Int32 nBlocks = ::std::min< sal_Int32 >( ornDays / nDaysInBlock, nMaxBlocks );
56 ornYear = static_cast< sal_Int16 >( ornYear + nYearsPerBlock * nBlocks );
57 ornDays -= nBlocks * nDaysInBlock;
58}
59
62sal_Int32 lclGetDays( const util::Date& rDate )
63{
64 // number of days in all full years before passed date including all leap days
65 sal_Int32 nDays = rDate.Year * 365 + ((rDate.Year + 3) / 4) - ((rDate.Year + 99) / 100) + ((rDate.Year + 399) / 400);
66 OSL_ENSURE( (1 <= rDate.Month) && (rDate.Month <= 12), "lclGetDays - invalid month" );
67 OSL_ENSURE( (1 <= rDate.Day) && (rDate.Day <= 31), "lclGetDays - invalid day" ); // yes, this is weak...
68 if( (1 <= rDate.Month) && (rDate.Month <= 12) )
69 {
70 // number of days at start of month jan feb mar apr may jun jul aug sep oct nov dec
71 static const sal_Int32 spnCumDays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
72 // add number of days in full months before passed date
73 nDays += spnCumDays[ rDate.Month - 1 ];
74 // add number of days from passed date (this adds one day too much)
75 nDays += rDate.Day;
76 /* Remove the one day added too much if there is no leap day before
77 the passed day in the passed year. This means: remove the day, if
78 we are in january or february (leap day not reached if existing),
79 or if the passed year is not a leap year. */
80 if( (rDate.Month < 3) || !lclIsLeapYear( rDate.Year ) )
81 --nDays;
82 }
83 return nDays;
84}
85
86} // namespace
87
89 WorkbookHelper( rHelper ),
90 mnNullDate( lclGetDays( util::Date( 30, 12, 1899 ) ) )
91{
92 // initialize constant and default coefficients
93 const DeviceInfo& rDeviceInfo = getBaseFilter().getGraphicHelper().getDeviceInfo();
96 maCoeffs[Unit::ScreenX] = o3tl::convert((rDeviceInfo.PixelPerMeterX > 0) ? (1000.0 / rDeviceInfo.PixelPerMeterX) : 0.5, o3tl::Length::mm, o3tl::Length::emu);
97 maCoeffs[Unit::ScreenY] = o3tl::convert((rDeviceInfo.PixelPerMeterY > 0) ? (1000.0 / rDeviceInfo.PixelPerMeterY) : 0.5, o3tl::Length::mm, o3tl::Length::emu);
98 maCoeffs[Unit::Digit] = o3tl::convert(2.0, o3tl::Length::mm, o3tl::Length::emu); // default: 1 digit = 2 mm
99 maCoeffs[Unit::Space] = o3tl::convert(1.0, o3tl::Length::mm, o3tl::Length::emu); // default 1 space = 1 mm
100
101 // error code maps
102 addErrorCode( BIFF_ERR_NULL, "#NULL!" );
103 addErrorCode( BIFF_ERR_DIV0, "#DIV/0!" );
104 addErrorCode( BIFF_ERR_VALUE, "#VALUE!" );
105 addErrorCode( BIFF_ERR_REF, "#REF!" );
106 addErrorCode( BIFF_ERR_NAME, "#NAME?" );
107 addErrorCode( BIFF_ERR_NUM, "#NUM!" );
108 addErrorCode( BIFF_ERR_NA, "#N/A" );
109}
110
112{
113 PropertySet aDocProps( getDocument() );
114 Reference< XDevice > xDevice( aDocProps.getAnyProperty( PROP_ReferenceDevice ), UNO_QUERY );
115 if( !xDevice.is() )
116 return;
117
118 // get character widths from default font
119 const oox::xls::Font* pDefFont = getStyles().getDefaultFont().get();
120 if( !pDefFont )
121 return;
122
123 // XDevice expects pixels in font descriptor, but font contains twips
124 const FontDescriptor& aDesc = pDefFont->getFontDescriptor();
125 Reference< XFont > xFont = xDevice->getFont( aDesc );
126 if( !xFont.is() )
127 return;
128
129 // get maximum width of all digits
130 sal_Int64 nDigitWidth = 0;
131 for( sal_Unicode cChar = '0'; cChar <= '9'; ++cChar )
132 nDigitWidth = ::std::max(nDigitWidth, o3tl::convert(xFont->getCharWidth(cChar),
134 if( nDigitWidth > 0 )
135 maCoeffs[ Unit::Digit ] = nDigitWidth;
136 // get width of space character
137 sal_Int64 nSpaceWidth
138 = o3tl::convert(xFont->getCharWidth(' '), o3tl::Length::twip, o3tl::Length::emu);
139 if( nSpaceWidth > 0 )
140 maCoeffs[ Unit::Space ] = nSpaceWidth;
141}
142
143void UnitConverter::finalizeNullDate( const util::Date& rNullDate )
144{
145 // convert the nulldate to number of days since 0000-Jan-01
146 mnNullDate = lclGetDays( rNullDate );
147}
148
149// conversion -----------------------------------------------------------------
150
151double UnitConverter::scaleValue( double fValue, Unit eFromUnit, Unit eToUnit ) const
152{
153 return (eFromUnit == eToUnit) ? fValue : (fValue * getCoefficient( eFromUnit ) / getCoefficient( eToUnit ));
154}
155
156double UnitConverter::calcSerialFromDateTime( const util::DateTime& rDateTime ) const
157{
158 sal_Int32 nDays = lclGetDays( util::Date( rDateTime.Day, rDateTime.Month, rDateTime.Year ) ) - mnNullDate;
159 OSL_ENSURE( nDays >= 0, "UnitConverter::calcDateTimeSerial - invalid date" );
160 OSL_ENSURE( (rDateTime.Hours <= 23) && (rDateTime.Minutes <= 59) && (rDateTime.Seconds <= 59), "UnitConverter::calcDateTimeSerial - invalid time" );
161 return nDays + rDateTime.Hours / 24.0 + rDateTime.Minutes / 1440.0 + rDateTime.Seconds / 86400.0;
162}
163
164util::DateTime UnitConverter::calcDateTimeFromSerial( double fSerial ) const
165{
166 util::DateTime aDateTime( 0, 0, 0, 0, 1, 1, 0, false );
167 double fDays = 0.0;
168 double fTime = modf( fSerial, &fDays );
169
170 // calculate date from number of days with O(1) complexity
171 sal_Int32 nDays = getLimitedValue< sal_Int32, double >( fDays + mnNullDate, 0, 3652424 );
172 // skip year 0, assumed to be a leap year. By starting at year 1, leap years can be handled easily
173 if( nDays >= 366 ) { ++aDateTime.Year; nDays -= 366; }
174 // skip full blocks of 400, 100, 4 years, and remaining full years
175 lclSkipYearBlock( nDays, aDateTime.Year, 400 * 365 + 97, 400, 24 );
176 lclSkipYearBlock( nDays, aDateTime.Year, 100 * 365 + 24, 100, 3 );
177 lclSkipYearBlock( nDays, aDateTime.Year, 4 * 365 + 1, 4, 24 );
178 lclSkipYearBlock( nDays, aDateTime.Year, 365, 1, 3 );
179 // skip full months of current year
180 static const sal_Int32 spnDaysInMonth[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
181 if( (nDays >= 59) && !lclIsLeapYear( aDateTime.Year ) ) ++nDays;
182 const sal_Int32* pnDaysInMonth = spnDaysInMonth;
183 while( *pnDaysInMonth >= nDays ) { ++aDateTime.Month; nDays -= *pnDaysInMonth; ++pnDaysInMonth; }
184 aDateTime.Day = static_cast< sal_uInt16 >( nDays + 1 );
185
186 // calculate time from fractional part of serial
187 sal_Int32 nTime = getLimitedValue< sal_Int32, double >( fTime * 86400, 0, 86399 );
188 aDateTime.Seconds = static_cast< sal_uInt16 >( nTime % 60 );
189 nTime /= 60;
190 aDateTime.Minutes = static_cast< sal_uInt16 >( nTime % 60 );
191 aDateTime.Hours = static_cast< sal_uInt16 >( nTime / 60 );
192
193 return aDateTime;
194}
195
196sal_uInt8 UnitConverter::calcBiffErrorCode( const OUString& rErrorCode ) const
197{
198 auto aIt = maOoxErrCodes.find( rErrorCode );
199 return (aIt == maOoxErrCodes.end()) ? BIFF_ERR_NA : aIt->second;
200}
201
202OUString UnitConverter::calcErrorString( sal_uInt8 nErrorCode ) const
203{
204 auto iFail( maOoxErrCodes.cend());
205 for (auto aIt( maOoxErrCodes.cbegin()); aIt != maOoxErrCodes.cend(); ++aIt)
206 {
207 if (aIt->second == nErrorCode)
208 return aIt->first;
209 if (aIt->second == BIFF_ERR_NA)
210 iFail = aIt;
211 }
212 assert(iFail != maOoxErrCodes.end()); // BIFF_ERR_NA really should be in the map...
213 return iFail != maOoxErrCodes.end() ? iFail->first : OUString();
214}
215
216void UnitConverter::addErrorCode( sal_uInt8 nErrorCode, const OUString& rErrorCode )
217{
218 maOoxErrCodes[ rErrorCode ] = nErrorCode;
219}
220
222{
223 return maCoeffs[ eUnit ];
224}
225
226} // namespace oox::xls
227
228/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const css::awt::DeviceInfo & getDeviceInfo() const
css::uno::Any getAnyProperty(sal_Int32 nPropId) const
GraphicHelper & getGraphicHelper() const
const css::awt::FontDescriptor & getFontDescriptor() const
Returns an API font descriptor with own font information.
FontRef getDefaultFont() const
Returns the default application font (used in the "Normal" cell style).
double calcSerialFromDateTime(const css::util::DateTime &rDateTime) const
Returns the serial value of the passed datetime, based on current nulldate.
void finalizeImport()
Final processing after import of all style settings.
void addErrorCode(sal_uInt8 nErrorCode, const OUString &rErrorCode)
Adds an error code to the internal maps.
sal_uInt8 calcBiffErrorCode(const OUString &rErrorCode) const
Returns a BIFF error code from the passed error string.
std::map< OUString, sal_uInt8 > maOoxErrCodes
Coefficients for unit conversion.
sal_Int32 mnNullDate
Maps error code strings to BIFF error constants.
o3tl::enumarray< Unit, double > maCoeffs
void finalizeNullDate(const css::util::Date &rNullDate)
Updates internal nulldate for date/serial conversion.
UnitConverter(const WorkbookHelper &rHelper)
double scaleValue(double fValue, Unit eFromUnit, Unit eToUnit) const
Converts the passed value between the passed units.
OUString calcErrorString(sal_uInt8 nErrorCode) const
Returns an error string from the passed BIFF error code.
css::util::DateTime calcDateTimeFromSerial(double fSerial) const
Returns the datetime of the passed serial value, based on current nulldate.
double getCoefficient(Unit eUnit) const
Returns the conversion coefficient for the passed unit.
Helper class to provide access to global workbook data.
StylesBuffer & getStyles() const
Returns all cell formatting objects read from the styles substream.
::oox::core::FilterBase & getBaseFilter() const
Returns the base filter object (base class of all filters).
const css::uno::Reference< css::sheet::XSpreadsheetDocument > & getDocument() const
Returns a reference to the source/target spreadsheet document model.
constexpr Point convert(const Point &rPoint, o3tl::Length eFrom, o3tl::Length eTo)
const sal_uInt8 BIFF_ERR_DIV0
Definition: biffhelper.hxx:561
const sal_uInt8 BIFF_ERR_NUM
Definition: biffhelper.hxx:565
const sal_uInt8 BIFF_ERR_NA
Definition: biffhelper.hxx:566
const sal_uInt8 BIFF_ERR_NULL
Common object settings.
Definition: biffhelper.hxx:560
const sal_uInt8 BIFF_ERR_VALUE
Definition: biffhelper.hxx:562
const sal_uInt8 BIFF_ERR_NAME
Definition: biffhelper.hxx:564
Unit
Units supported by the UnitConverter class.
@ ScreenX
English Metric Unit (1/360,000 cm).
@ Emu
Twips (1/20 point).
@ Digit
Vertical screen pixels.
@ ScreenY
Horizontal screen pixels.
@ Space
Digit width of document default font.
const sal_uInt8 BIFF_ERR_REF
Definition: biffhelper.hxx:563
unsigned char sal_uInt8
sal_uInt16 sal_Unicode