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>
29 #include <o3tl/unit_conversion.hxx>
30 #include <osl/diagnose.h>
31 #include <oox/core/filterbase.hxx>
33 #include <oox/token/properties.hxx>
34 #include <stylesbuffer.hxx>
35 #include <biffhelper.hxx>
36 
37 namespace com::sun::star::awt { struct FontDescriptor; }
38 
39 namespace oox::xls {
40 
41 using namespace ::com::sun::star;
42 using namespace ::com::sun::star::awt;
43 using namespace ::com::sun::star::uno;
44 
45 namespace {
46 
48 bool lclIsLeapYear( sal_Int32 nYear )
49 {
50  return ((nYear % 4) == 0) && (((nYear % 100) != 0) || ((nYear % 400) == 0));
51 }
52 
53 void 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 
62 sal_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();
98  maCoeffs[ Unit::ScreenX ] = (rDeviceInfo.PixelPerMeterX > 0) ? (100000.0 / rDeviceInfo.PixelPerMeterX) : 50.0;
99  maCoeffs[ Unit::ScreenY ] = (rDeviceInfo.PixelPerMeterY > 0) ? (100000.0 / rDeviceInfo.PixelPerMeterY) : 50.0;
100  maCoeffs[ Unit::Digit ] = 200.0; // default: 1 digit = 2 mm
101  maCoeffs[ Unit::Space ] = 100.0; // default 1 space = 1 mm
102 
103  // error code maps
104  addErrorCode( BIFF_ERR_NULL, "#NULL!" );
105  addErrorCode( BIFF_ERR_DIV0, "#DIV/0!" );
106  addErrorCode( BIFF_ERR_VALUE, "#VALUE!" );
107  addErrorCode( BIFF_ERR_REF, "#REF!" );
108  addErrorCode( BIFF_ERR_NAME, "#NAME?" );
109  addErrorCode( BIFF_ERR_NUM, "#NUM!" );
110  addErrorCode( BIFF_ERR_NA, "#N/A" );
111 }
112 
114 {
115  PropertySet aDocProps( getDocument() );
116  Reference< XDevice > xDevice( aDocProps.getAnyProperty( PROP_ReferenceDevice ), UNO_QUERY );
117  if( !xDevice.is() )
118  return;
119 
120  // get character widths from default font
121  const oox::xls::Font* pDefFont = getStyles().getDefaultFont().get();
122  if( !pDefFont )
123  return;
124 
125  // XDevice expects pixels in font descriptor, but font contains twips
126  const FontDescriptor& aDesc = pDefFont->getFontDescriptor();
127  Reference< XFont > xFont = xDevice->getFont( aDesc );
128  if( !xFont.is() )
129  return;
130 
131  // get maximum width of all digits
132  sal_Int64 nDigitWidth = 0;
133  for( sal_Unicode cChar = '0'; cChar <= '9'; ++cChar )
134  nDigitWidth
135  = ::std::max(nDigitWidth, o3tl::convert(xFont->getCharWidth(cChar), o3tl::Length::twip,
137  if( nDigitWidth > 0 )
138  maCoeffs[ Unit::Digit ] = nDigitWidth;
139  // get width of space character
140  sal_Int32 nSpaceWidth
141  = o3tl::convert(xFont->getCharWidth(' '), o3tl::Length::twip, o3tl::Length::mm100);
142  if( nSpaceWidth > 0 )
143  maCoeffs[ Unit::Space ] = nSpaceWidth;
144 }
145 
146 void UnitConverter::finalizeNullDate( const util::Date& rNullDate )
147 {
148  // convert the nulldate to number of days since 0000-Jan-01
149  mnNullDate = lclGetDays( rNullDate );
150 }
151 
152 // conversion -----------------------------------------------------------------
153 
154 double UnitConverter::scaleValue( double fValue, Unit eFromUnit, Unit eToUnit ) const
155 {
156  return (eFromUnit == eToUnit) ? fValue : (fValue * getCoefficient( eFromUnit ) / getCoefficient( eToUnit ));
157 }
158 
159 sal_Int32 UnitConverter::scaleToMm100( double fValue, Unit eUnit ) const
160 {
161  return static_cast< sal_Int32 >( fValue * getCoefficient( eUnit ) + 0.5 );
162 }
163 
164 double UnitConverter::scaleFromMm100( sal_Int32 nMm100, Unit eUnit ) const
165 {
166  return static_cast< double >( nMm100 ) / getCoefficient( eUnit );
167 }
168 
169 double UnitConverter::calcSerialFromDateTime( const util::DateTime& rDateTime ) const
170 {
171  sal_Int32 nDays = lclGetDays( util::Date( rDateTime.Day, rDateTime.Month, rDateTime.Year ) ) - mnNullDate;
172  OSL_ENSURE( nDays >= 0, "UnitConverter::calcDateTimeSerial - invalid date" );
173  OSL_ENSURE( (rDateTime.Hours <= 23) && (rDateTime.Minutes <= 59) && (rDateTime.Seconds <= 59), "UnitConverter::calcDateTimeSerial - invalid time" );
174  return nDays + rDateTime.Hours / 24.0 + rDateTime.Minutes / 1440.0 + rDateTime.Seconds / 86400.0;
175 }
176 
177 util::DateTime UnitConverter::calcDateTimeFromSerial( double fSerial ) const
178 {
179  util::DateTime aDateTime( 0, 0, 0, 0, 1, 1, 0, false );
180  double fDays = 0.0;
181  double fTime = modf( fSerial, &fDays );
182 
183  // calculate date from number of days with O(1) complexity
184  sal_Int32 nDays = getLimitedValue< sal_Int32, double >( fDays + mnNullDate, 0, 3652424 );
185  // skip year 0, assumed to be a leap year. By starting at year 1, leap years can be handled easily
186  if( nDays >= 366 ) { ++aDateTime.Year; nDays -= 366; }
187  // skip full blocks of 400, 100, 4 years, and remaining full years
188  lclSkipYearBlock( nDays, aDateTime.Year, 400 * 365 + 97, 400, 24 );
189  lclSkipYearBlock( nDays, aDateTime.Year, 100 * 365 + 24, 100, 3 );
190  lclSkipYearBlock( nDays, aDateTime.Year, 4 * 365 + 1, 4, 24 );
191  lclSkipYearBlock( nDays, aDateTime.Year, 365, 1, 3 );
192  // skip full months of current year
193  static const sal_Int32 spnDaysInMonth[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
194  if( (nDays >= 59) && !lclIsLeapYear( aDateTime.Year ) ) ++nDays;
195  const sal_Int32* pnDaysInMonth = spnDaysInMonth;
196  while( *pnDaysInMonth >= nDays ) { ++aDateTime.Month; nDays -= *pnDaysInMonth; ++pnDaysInMonth; }
197  aDateTime.Day = static_cast< sal_uInt16 >( nDays + 1 );
198 
199  // calculate time from fractional part of serial
200  sal_Int32 nTime = getLimitedValue< sal_Int32, double >( fTime * 86400, 0, 86399 );
201  aDateTime.Seconds = static_cast< sal_uInt16 >( nTime % 60 );
202  nTime /= 60;
203  aDateTime.Minutes = static_cast< sal_uInt16 >( nTime % 60 );
204  aDateTime.Hours = static_cast< sal_uInt16 >( nTime / 60 );
205 
206  return aDateTime;
207 }
208 
209 sal_uInt8 UnitConverter::calcBiffErrorCode( const OUString& rErrorCode ) const
210 {
211  auto aIt = maOoxErrCodes.find( rErrorCode );
212  return (aIt == maOoxErrCodes.end()) ? BIFF_ERR_NA : aIt->second;
213 }
214 
215 OUString UnitConverter::calcErrorString( sal_uInt8 nErrorCode ) const
216 {
217  auto iFail( maOoxErrCodes.cend());
218  for (auto aIt( maOoxErrCodes.cbegin()); aIt != maOoxErrCodes.cend(); ++aIt)
219  {
220  if (aIt->second == nErrorCode)
221  return aIt->first;
222  if (aIt->second == BIFF_ERR_NA)
223  iFail = aIt;
224  }
225  assert(iFail != maOoxErrCodes.end()); // BIFF_ERR_NA really should be in the map...
226  return iFail != maOoxErrCodes.end() ? iFail->first : OUString();
227 }
228 
229 void UnitConverter::addErrorCode( sal_uInt8 nErrorCode, const OUString& rErrorCode )
230 {
231  maOoxErrCodes[ rErrorCode ] = nErrorCode;
232 }
233 
234 double UnitConverter::getCoefficient( Unit eUnit ) const
235 {
236  return maCoeffs[ eUnit ];
237 }
238 
239 } // namespace oox::xls
240 
241 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
exports com.sun.star.lib. util
Helper class to provide access to global workbook data.
double scaleValue(double fValue, Unit eFromUnit, Unit eToUnit) const
Converts the passed value between the passed units.
const sal_uInt8 BIFF_ERR_VALUE
Definition: biffhelper.hxx:562
const sal_uInt8 BIFF_ERR_NA
Definition: biffhelper.hxx:566
const css::awt::DeviceInfo & getDeviceInfo() const
const sal_uInt8 BIFF_ERR_REF
Definition: biffhelper.hxx:563
constexpr Point convert(const Point &rPoint, o3tl::Length eFrom, o3tl::Length eTo)
css::uno::Any getAnyProperty(sal_Int32 nPropId) const
const sal_uInt8 BIFF_ERR_NAME
Definition: biffhelper.hxx:564
StylesBuffer & getStyles() const
Returns all cell formatting objects read from the styles substream.
sal_Int32 scaleToMm100(double fValue, Unit eUnit) const
Converts the passed value to 1/100 millimeters.
GraphicHelper & getGraphicHelper() const
double calcSerialFromDateTime(const css::util::DateTime &rDateTime) const
Returns the serial value of the passed datetime, based on current nulldate.
sal_uInt16 sal_Unicode
css::util::DateTime calcDateTimeFromSerial(double fSerial) const
Returns the datetime of the passed serial value, based on current nulldate.
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
::oox::core::FilterBase & getBaseFilter() const
Returns the base filter object (base class of all filters).
std::map< OUString, sal_uInt8 > maOoxErrCodes
Coefficients for unit conversion.
OUString calcErrorString(sal_uInt8 nErrorCode) const
Returns an error string from the passed BIFF error code.
void finalizeNullDate(const css::util::Date &rNullDate)
Updates internal nulldate for date/serial conversion.
Unit
Units supported by the UnitConverter class.
const css::uno::Reference< css::sheet::XSpreadsheetDocument > & getDocument() const
Returns a reference to the source/target spreadsheet document model.
void addErrorCode(sal_uInt8 nErrorCode, const OUString &rErrorCode)
Adds an error code to the internal maps.
const sal_uInt8 BIFF_ERR_DIV0
Definition: biffhelper.hxx:561
Twips (1/20 point).
const sal_uInt8 BIFF_ERR_NUM
Definition: biffhelper.hxx:565
Digit width of document default font.
const css::awt::FontDescriptor & getFontDescriptor() const
Returns an API font descriptor with own font information.
void finalizeImport()
Final processing after import of all style settings.
unsigned char sal_uInt8
Horizontal screen pixels.
English Metric Unit (1/360,000 cm).
UnitConverter(const WorkbookHelper &rHelper)
const sal_uInt8 BIFF_ERR_NULL
Common object settings.
Definition: biffhelper.hxx:560
double scaleFromMm100(sal_Int32 nMm100, Unit eUnit) const
Converts the passed value from 1/100 millimeters to the passed unit.
sal_uInt8 calcBiffErrorCode(const OUString &rErrorCode) const
Returns a BIFF error code from the passed error string.
o3tl::enumarray< Unit, double > maCoeffs
sal_Int32 mnNullDate
Maps error code strings to BIFF error constants.
FontRef getDefaultFont() const
Returns the default application font (used in the "Normal" cell style).
double getCoefficient(Unit eUnit) const
Returns the conversion coefficient for the passed unit.
Vertical screen pixels.