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 <memory>
21 #include <xlroot.hxx>
22 #include <sal/log.hxx>
23 #include <com/sun/star/awt/XDevice.hpp>
24 #include <com/sun/star/frame/Desktop.hpp>
25 #include <com/sun/star/frame/XFrame.hpp>
26 #include <com/sun/star/i18n/ScriptType.hpp>
27 #include <comphelper/processfactory.hxx>
28 #include <comphelper/servicehelper.hxx>
29 #include <sot/storage.hxx>
30 #include <vcl/svapp.hxx>
31 #include <svl/stritem.hxx>
32 #include <svl/languageoptions.hxx>
33 #include <sfx2/objsh.hxx>
34 #include <sfx2/docfile.hxx>
35 #include <sfx2/sfxsids.hrc>
36 #include <vcl/font.hxx>
37 #include <vcl/settings.hxx>
38 #include <tools/diagnose_ex.h>
39 
40 #include <editeng/editstat.hxx>
41 #include <scitems.hxx>
42 #include <editeng/eeitem.hxx>
43 #include <editeng/fhgtitem.hxx>
44 #include <document.hxx>
45 #include <docpool.hxx>
46 #include <docuno.hxx>
47 #include <editutil.hxx>
48 #include <drwlayer.hxx>
49 #include <scextopt.hxx>
50 #include <patattr.hxx>
51 #include <fapihelper.hxx>
52 #include <xlconst.hxx>
53 #include <xlstyle.hxx>
54 #include <xlchart.hxx>
55 #include <xltracer.hxx>
56 #include <xltools.hxx>
57 #include <unotools/configmgr.hxx>
58 #include <unotools/useroptions.hxx>
59 #include <root.hxx>
60 
61 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
62 
63 using ::com::sun::star::uno::Exception;
64 using ::com::sun::star::uno::Reference;
65 using ::com::sun::star::uno::UNO_QUERY_THROW;
66 using ::com::sun::star::uno::UNO_SET_THROW;
67 using ::com::sun::star::awt::XDevice;
68 using ::com::sun::star::awt::DeviceInfo;
69 using ::com::sun::star::frame::XFrame;
70 
71 using namespace ::com::sun::star;
72 
73 // Global data ================================================================
74 
75 #ifdef DBG_UTIL
~XclDebugObjCounter()76 XclDebugObjCounter::~XclDebugObjCounter()
77 {
78     OSL_ENSURE( mnObjCnt == 0, "XclDebugObjCounter::~XclDebugObjCounter - wrong root object count" );
79 }
80 #endif
81 
XclRootData(XclBiff eBiff,SfxMedium & rMedium,tools::SvRef<SotStorage> const & xRootStrg,ScDocument & rDoc,rtl_TextEncoding eTextEnc,bool bExport)82 XclRootData::XclRootData( XclBiff eBiff, SfxMedium& rMedium,
83         tools::SvRef<SotStorage> const & xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc, bool bExport ) :
84     meBiff( eBiff ),
85     meOutput( EXC_OUTPUT_BINARY ),
86     mrMedium( rMedium ),
87     mxRootStrg( xRootStrg ),
88     mrDoc( rDoc ),
89     meTextEnc( eTextEnc ),
90     meSysLang( Application::GetSettings().GetLanguageTag().getLanguageType() ),
91     meDocLang( Application::GetSettings().GetLanguageTag().getLanguageType() ),
92     meUILang( Application::GetSettings().GetUILanguageTag().getLanguageType() ),
93     mnDefApiScript( ApiScriptType::LATIN ),
94     maScMaxPos( mrDoc.MaxCol(), mrDoc.MaxRow(), MAXTAB ),
95     maXclMaxPos( EXC_MAXCOL2, EXC_MAXROW2, EXC_MAXTAB2 ),
96     maMaxPos( EXC_MAXCOL2, EXC_MAXROW2, EXC_MAXTAB2 ),
97     mxFontPropSetHlp( std::make_shared<XclFontPropSetHelper>() ),
98     mxChPropSetHlp( std::make_shared<XclChPropSetHelper>() ),
99     mxRD( std::make_shared<RootData>() ),
100     mfScreenPixelX( 50.0 ),
101     mfScreenPixelY( 50.0 ),
102     mnCharWidth( 110 ),
103     mnSpaceWidth(45),
104     mnScTab( 0 ),
105     mbExport( bExport )
106 {
107     if (!utl::ConfigManager::IsFuzzing())
108         maUserName = SvtUserOptions().GetLastName();
109     if (maUserName.isEmpty())
110         maUserName = "Calc";
111 
112     switch( ScGlobal::GetDefaultScriptType() )
113     {
114         case SvtScriptType::LATIN:      mnDefApiScript = ApiScriptType::LATIN;      break;
115         case SvtScriptType::ASIAN:      mnDefApiScript = ApiScriptType::ASIAN;      break;
116         case SvtScriptType::COMPLEX:    mnDefApiScript = ApiScriptType::COMPLEX;    break;
117         default:    SAL_WARN( "sc", "XclRootData::XclRootData - unknown script type" );
118     }
119 
120     // maximum cell position
121     switch( meBiff )
122     {
123         case EXC_BIFF2: maXclMaxPos.Set( EXC_MAXCOL2, EXC_MAXROW2, EXC_MAXTAB2 );   break;
124         case EXC_BIFF3: maXclMaxPos.Set( EXC_MAXCOL3, EXC_MAXROW3, EXC_MAXTAB3 );   break;
125         case EXC_BIFF4: maXclMaxPos.Set( EXC_MAXCOL4, EXC_MAXROW4, EXC_MAXTAB4 );   break;
126         case EXC_BIFF5: maXclMaxPos.Set( EXC_MAXCOL5, EXC_MAXROW5, EXC_MAXTAB5 );   break;
127         case EXC_BIFF8: maXclMaxPos.Set( EXC_MAXCOL8, EXC_MAXROW8, EXC_MAXTAB8 );   break;
128         default:        DBG_ERROR_BIFF();
129     }
130     maMaxPos.SetCol( ::std::min( maScMaxPos.Col(), maXclMaxPos.Col() ) );
131     maMaxPos.SetRow( ::std::min( maScMaxPos.Row(), maXclMaxPos.Row() ) );
132     maMaxPos.SetTab( ::std::min( maScMaxPos.Tab(), maXclMaxPos.Tab() ) );
133 
134     // document URL and path
135     if( const SfxItemSet* pItemSet = mrMedium.GetItemSet() )
136         if( const SfxStringItem* pItem = pItemSet->GetItem<SfxStringItem>( SID_FILE_NAME ) )
137             maDocUrl = pItem->GetValue();
138     maBasePath = maDocUrl.copy( 0, maDocUrl.lastIndexOf( '/' ) + 1 );
139 
140     // extended document options - always own object, try to copy existing data from document
141     if( const ScExtDocOptions* pOldDocOpt = mrDoc.GetExtDocOptions() )
142         mxExtDocOpt = std::make_shared<ScExtDocOptions>( *pOldDocOpt );
143     else
144         mxExtDocOpt = std::make_shared<ScExtDocOptions>();
145 
146     // screen pixel size
147     try
148     {
149         Reference< frame::XDesktop2 > xFramesSupp = frame::Desktop::create( ::comphelper::getProcessComponentContext() );
150         Reference< XFrame > xFrame( xFramesSupp->getActiveFrame(), UNO_SET_THROW );
151         Reference< XDevice > xDevice( xFrame->getContainerWindow(), UNO_QUERY_THROW );
152         DeviceInfo aDeviceInfo = xDevice->getInfo();
153         mfScreenPixelX = (aDeviceInfo.PixelPerMeterX > 0) ? (100000.0 / aDeviceInfo.PixelPerMeterX) : 50.0;
154         mfScreenPixelY = (aDeviceInfo.PixelPerMeterY > 0) ? (100000.0 / aDeviceInfo.PixelPerMeterY) : 50.0;
155     }
156     catch( const Exception&)
157     {
158         TOOLS_WARN_EXCEPTION( "sc", "XclRootData::XclRootData - cannot get output device info");
159     }
160 }
161 
~XclRootData()162 XclRootData::~XclRootData()
163 {
164 }
165 
XclRoot(XclRootData & rRootData)166 XclRoot::XclRoot( XclRootData& rRootData ) :
167     mrData( rRootData )
168 {
169 #if defined(DBG_UTIL) && OSL_DEBUG_LEVEL > 0
170     ++mrData.mnObjCnt;
171 #endif
172 
173     // filter tracer
174     mrData.mxTracer = std::make_shared<XclTracer>( GetDocUrl() );
175 }
176 
XclRoot(const XclRoot & rRoot)177 XclRoot::XclRoot( const XclRoot& rRoot ) :
178     mrData( rRoot.mrData )
179 {
180 #if defined(DBG_UTIL) && OSL_DEBUG_LEVEL > 0
181     ++mrData.mnObjCnt;
182 #endif
183 }
184 
~XclRoot()185 XclRoot::~XclRoot()
186 {
187 #if defined(DBG_UTIL) && OSL_DEBUG_LEVEL > 0
188     --mrData.mnObjCnt;
189 #endif
190 }
191 
operator =(const XclRoot & rRoot)192 XclRoot& XclRoot::operator=( const XclRoot& rRoot )
193 {
194     (void)rRoot;    // avoid compiler warning
195     // allowed for assignment in derived classes - but test if the same root data is used
196     OSL_ENSURE( &mrData == &rRoot.mrData, "XclRoot::operator= - incompatible root data" );
197     return *this;
198 }
199 
SetTextEncoding(rtl_TextEncoding eTextEnc)200 void XclRoot::SetTextEncoding( rtl_TextEncoding eTextEnc )
201 {
202     if( eTextEnc != RTL_TEXTENCODING_DONTKNOW )
203         mrData.meTextEnc = eTextEnc;
204 }
205 
SetCharWidth(const XclFontData & rFontData)206 void XclRoot::SetCharWidth( const XclFontData& rFontData )
207 {
208     mrData.mnCharWidth = 0;
209     if( OutputDevice* pPrinter = GetPrinter() )
210     {
211         vcl::Font aFont( rFontData.maName, Size( 0, rFontData.mnHeight ) );
212         aFont.SetFamily( rFontData.GetScFamily( GetTextEncoding() ) );
213         aFont.SetCharSet( rFontData.GetFontEncoding() );
214         aFont.SetWeight( rFontData.GetScWeight() );
215         pPrinter->SetFont( aFont );
216         // Usually digits have the same width, but in some fonts they don't ...
217         // Match the import in sc/source/filter/oox/unitconverter.cxx
218         // UnitConverter::finalizeImport()
219         for (sal_Unicode cChar = '0'; cChar <= '9'; ++cChar)
220             mrData.mnCharWidth = std::max( pPrinter->GetTextWidth( OUString(cChar)), mrData.mnCharWidth);
221 
222         // Set the width of space ' ' character.
223         mrData.mnSpaceWidth = pPrinter->GetTextWidth(OUString(' '));
224     }
225     if( mrData.mnCharWidth <= 0 )
226     {
227         // #i48717# Win98 with HP LaserJet returns 0
228         SAL_WARN( "sc", "XclRoot::SetCharWidth - invalid character width (no printer?)" );
229         mrData.mnCharWidth = 11 * rFontData.mnHeight / 20;
230     }
231     if (mrData.mnSpaceWidth <= 0)
232     {
233         SAL_WARN( "sc", "XclRoot::SetCharWidth - invalid character width (no printer?)" );
234         mrData.mnSpaceWidth = 45;
235     }
236 }
237 
GetHmmFromPixelX(double fPixelX) const238 sal_Int32 XclRoot::GetHmmFromPixelX( double fPixelX ) const
239 {
240     return static_cast< sal_Int32 >( fPixelX * mrData.mfScreenPixelX + 0.5 );
241 }
242 
GetHmmFromPixelY(double fPixelY) const243 sal_Int32 XclRoot::GetHmmFromPixelY( double fPixelY ) const
244 {
245     return static_cast< sal_Int32 >( fPixelY * mrData.mfScreenPixelY + 0.5 );
246 }
247 
RequestEncryptionData(::comphelper::IDocPasswordVerifier & rVerifier) const248 uno::Sequence< beans::NamedValue > XclRoot::RequestEncryptionData( ::comphelper::IDocPasswordVerifier& rVerifier ) const
249 {
250     ::std::vector< OUString > aDefaultPasswords;
251     aDefaultPasswords.push_back( XclRootData::gaDefPassword );
252     return ScfApiHelper::QueryEncryptionDataForMedium( mrData.mrMedium, rVerifier, &aDefaultPasswords );
253 }
254 
HasVbaStorage() const255 bool XclRoot::HasVbaStorage() const
256 {
257     tools::SvRef<SotStorage> xRootStrg = GetRootStorage();
258     return xRootStrg.is() && xRootStrg->IsContained( EXC_STORAGE_VBA_PROJECT );
259 }
260 
OpenStorage(tools::SvRef<SotStorage> const & xStrg,const OUString & rStrgName) const261 tools::SvRef<SotStorage> XclRoot::OpenStorage( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrgName ) const
262 {
263     return mrData.mbExport ?
264         ScfTools::OpenStorageWrite( xStrg, rStrgName ) :
265         ScfTools::OpenStorageRead( xStrg, rStrgName );
266 }
267 
OpenStorage(const OUString & rStrgName) const268 tools::SvRef<SotStorage> XclRoot::OpenStorage( const OUString& rStrgName ) const
269 {
270     return OpenStorage( GetRootStorage(), rStrgName );
271 }
272 
OpenStream(tools::SvRef<SotStorage> const & xStrg,const OUString & rStrmName) const273 tools::SvRef<SotStorageStream> XclRoot::OpenStream( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrmName ) const
274 {
275     return mrData.mbExport ?
276         ScfTools::OpenStorageStreamWrite( xStrg, rStrmName ) :
277         ScfTools::OpenStorageStreamRead( xStrg, rStrmName );
278 }
279 
OpenStream(const OUString & rStrmName) const280 tools::SvRef<SotStorageStream> XclRoot::OpenStream( const OUString& rStrmName ) const
281 {
282     return OpenStream( GetRootStorage(), rStrmName );
283 }
284 
GetDoc() const285 ScDocument& XclRoot::GetDoc() const
286 {
287     return mrData.mrDoc;
288 }
289 
GetDocShell() const290 SfxObjectShell* XclRoot::GetDocShell() const
291 {
292     return GetDoc().GetDocumentShell();
293 }
294 
GetDocModelObj() const295 ScModelObj* XclRoot::GetDocModelObj() const
296 {
297     SfxObjectShell* pDocShell = GetDocShell();
298     return pDocShell ? comphelper::getUnoTunnelImplementation<ScModelObj>( pDocShell->GetModel() ) : nullptr;
299 }
300 
GetPrinter() const301 OutputDevice* XclRoot::GetPrinter() const
302 {
303     return GetDoc().GetRefDevice();
304 }
305 
GetStyleSheetPool() const306 ScStyleSheetPool& XclRoot::GetStyleSheetPool() const
307 {
308     return *GetDoc().GetStyleSheetPool();
309 }
310 
GetNamedRanges() const311 ScRangeName& XclRoot::GetNamedRanges() const
312 {
313     return *GetDoc().GetRangeName();
314 }
315 
GetSdrPage(SCTAB nScTab) const316 SdrPage* XclRoot::GetSdrPage( SCTAB nScTab ) const
317 {
318     return ((nScTab >= 0) && GetDoc().GetDrawLayer()) ?
319         GetDoc().GetDrawLayer()->GetPage( static_cast< sal_uInt16 >( nScTab ) ) : nullptr;
320 }
321 
GetFormatter() const322 SvNumberFormatter& XclRoot::GetFormatter() const
323 {
324     return *GetDoc().GetFormatTable();
325 }
326 
GetNullDate() const327 DateTime XclRoot::GetNullDate() const
328 {
329     return GetFormatter().GetNullDate();
330 }
331 
GetBaseYear() const332 sal_uInt16 XclRoot::GetBaseYear() const
333 {
334     // return 1904 for 1904-01-01, and 1900 for 1899-12-30
335     return (GetNullDate().GetYear() == 1904) ? 1904 : 1900;
336 }
337 
338 const DateTime theOurCompatNullDate( Date( 30, 12, 1899 ));
339 const DateTime theExcelCutOverDate( Date( 1, 3, 1900 ));
340 
GetDoubleFromDateTime(const DateTime & rDateTime) const341 double XclRoot::GetDoubleFromDateTime( const DateTime& rDateTime ) const
342 {
343     double fValue = rDateTime - GetNullDate();
344     // adjust dates before 1900-03-01 to get correct time values in the range [0.0,1.0)
345     /* XXX: this is only used when reading BIFF, otherwise we'd have to check
346      * for dateCompatibility==true as mentioned below. */
347     if( rDateTime < theExcelCutOverDate && GetNullDate() == theOurCompatNullDate )
348         fValue -= 1.0;
349     return fValue;
350 }
351 
GetDateTimeFromDouble(double fValue) const352 DateTime XclRoot::GetDateTimeFromDouble( double fValue ) const
353 {
354     DateTime aDateTime = GetNullDate() + fValue;
355     // adjust dates before 1900-03-01 to get correct time values
356     /* FIXME: correction should only be done when writing BIFF or OOXML
357      * transitional with dateCompatibility==true (or absent for default true),
358      * but not if strict ISO/IEC 29500 which does not have the Excel error
359      * compatibility and the null date is the same 1899-12-30 as ours. */
360     if( aDateTime < theExcelCutOverDate && GetNullDate() == theOurCompatNullDate )
361         aDateTime.AddDays(1);
362     return aDateTime;
363 }
364 
GetEditEngine() const365 ScEditEngineDefaulter& XclRoot::GetEditEngine() const
366 {
367     if( !mrData.mxEditEngine )
368     {
369         mrData.mxEditEngine = std::make_shared<ScEditEngineDefaulter>( GetDoc().GetEnginePool() );
370         ScEditEngineDefaulter& rEE = *mrData.mxEditEngine;
371         rEE.SetRefMapMode(MapMode(MapUnit::Map100thMM));
372         rEE.SetEditTextObjectPool( GetDoc().GetEditPool() );
373         rEE.SetUpdateMode( false );
374         rEE.EnableUndo( false );
375         rEE.SetControlWord( rEE.GetControlWord() & ~EEControlBits::ALLOWBIGOBJS );
376     }
377     return *mrData.mxEditEngine;
378 }
379 
GetHFEditEngine() const380 ScHeaderEditEngine& XclRoot::GetHFEditEngine() const
381 {
382     if( !mrData.mxHFEditEngine )
383     {
384         mrData.mxHFEditEngine = std::make_shared<ScHeaderEditEngine>( EditEngine::CreatePool().get() );
385         ScHeaderEditEngine& rEE = *mrData.mxHFEditEngine;
386         rEE.SetRefMapMode(MapMode(MapUnit::MapTwip)); // headers/footers use twips as default metric
387         rEE.SetUpdateMode( false );
388         rEE.EnableUndo( false );
389         rEE.SetControlWord( rEE.GetControlWord() & ~EEControlBits::ALLOWBIGOBJS );
390 
391         // set Calc header/footer defaults
392         auto pEditSet = std::make_unique<SfxItemSet>( rEE.GetEmptyItemSet() );
393         SfxItemSet aItemSet( *GetDoc().GetPool(), svl::Items<ATTR_PATTERN_START, ATTR_PATTERN_END>{} );
394         ScPatternAttr::FillToEditItemSet( *pEditSet, aItemSet );
395         // FillToEditItemSet() adjusts font height to 1/100th mm, we need twips
396         pEditSet->Put( aItemSet.Get( ATTR_FONT_HEIGHT ).CloneSetWhich(EE_CHAR_FONTHEIGHT) );
397         pEditSet->Put( aItemSet.Get( ATTR_CJK_FONT_HEIGHT ).CloneSetWhich(EE_CHAR_FONTHEIGHT_CJK) );
398         pEditSet->Put( aItemSet.Get( ATTR_CTL_FONT_HEIGHT ).CloneSetWhich(EE_CHAR_FONTHEIGHT_CTL) );
399         rEE.SetDefaults( std::move(pEditSet) );    // takes ownership
400    }
401     return *mrData.mxHFEditEngine;
402 }
403 
GetDrawEditEngine() const404 EditEngine& XclRoot::GetDrawEditEngine() const
405 {
406     if( !mrData.mxDrawEditEng )
407     {
408         mrData.mxDrawEditEng = std::make_shared<EditEngine>( &GetDoc().GetDrawLayer()->GetItemPool() );
409         EditEngine& rEE = *mrData.mxDrawEditEng;
410         rEE.SetRefMapMode(MapMode(MapUnit::Map100thMM));
411         rEE.SetUpdateMode( false );
412         rEE.EnableUndo( false );
413         rEE.SetControlWord( rEE.GetControlWord() & ~EEControlBits::ALLOWBIGOBJS );
414     }
415     return *mrData.mxDrawEditEng;
416 }
417 
GetFontPropSetHelper() const418 XclFontPropSetHelper& XclRoot::GetFontPropSetHelper() const
419 {
420     return *mrData.mxFontPropSetHlp;
421 }
422 
GetChartPropSetHelper() const423 XclChPropSetHelper& XclRoot::GetChartPropSetHelper() const
424 {
425     return *mrData.mxChPropSetHlp;
426 }
427 
GetExtDocOptions() const428 ScExtDocOptions& XclRoot::GetExtDocOptions() const
429 {
430     return *mrData.mxExtDocOpt;
431 }
432 
GetTracer() const433 XclTracer& XclRoot::GetTracer() const
434 {
435     return *mrData.mxTracer;
436 }
437 
438 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
439