LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / sc / source / filter / excel / xestream.cxx
blob315b905560f3680cd5f2c6c92bb73eb288f13bc7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <stdio.h>
21 #include <string.h>
22 #include <utility>
24 #include <filter/msfilter/util.hxx>
25 #include <o3tl/safeint.hxx>
26 #include <rtl/ustring.hxx>
27 #include <rtl/ustrbuf.hxx>
28 #include <rtl/random.h>
29 #include <sax/fshelper.hxx>
30 #include <unotools/streamwrap.hxx>
31 #include <sot/storage.hxx>
32 #include <tools/urlobj.hxx>
33 #include <vcl/svapp.hxx>
34 #include <vcl/settings.hxx>
35 #include <officecfg/Office/Calc.hxx>
37 #include <docuno.hxx>
38 #include <xestream.hxx>
39 #include <xladdress.hxx>
40 #include <xlstring.hxx>
41 #include <xltools.hxx>
42 #include <xeroot.hxx>
43 #include <xcl97rec.hxx>
44 #include <rangelst.hxx>
45 #include <compiler.hxx>
46 #include <formulacell.hxx>
47 #include <tokenarray.hxx>
48 #include <tokenstringcontext.hxx>
49 #include <refreshtimerprotector.hxx>
50 #include <globstr.hrc>
51 #include <scresid.hxx>
52 #include <root.hxx>
53 #include <sfx2/app.hxx>
55 #include <docsh.hxx>
56 #include <viewdata.hxx>
57 #include <excdoc.hxx>
59 #include <oox/token/tokens.hxx>
60 #include <oox/token/relationship.hxx>
61 #include <oox/export/utils.hxx>
62 #include <formula/grammar.hxx>
63 #include <oox/ole/vbaexport.hxx>
64 #include <excelvbaproject.hxx>
66 #include <com/sun/star/task/XStatusIndicator.hpp>
67 #include <memory>
68 #include <comphelper/storagehelper.hxx>
70 #include <externalrefmgr.hxx>
72 #define DEBUG_XL_ENCRYPTION 0
74 using ::com::sun::star::uno::XInterface;
75 using ::std::vector;
77 using namespace com::sun::star;
78 using namespace ::com::sun::star::beans;
79 using namespace ::com::sun::star::io;
80 using namespace ::com::sun::star::lang;
81 using namespace ::com::sun::star::sheet;
82 using namespace ::com::sun::star::uno;
83 using namespace ::formula;
84 using namespace ::oox;
86 XclExpStream::XclExpStream( SvStream& rOutStrm, const XclExpRoot& rRoot, sal_uInt16 nMaxRecSize ) :
87 mrStrm( rOutStrm ),
88 mrRoot( rRoot ),
89 mbUseEncrypter( false ),
90 mnMaxRecSize( nMaxRecSize ),
91 mnCurrMaxSize( 0 ),
92 mnMaxSliceSize( 0 ),
93 mnHeaderSize( 0 ),
94 mnCurrSize( 0 ),
95 mnSliceSize( 0 ),
96 mnPredictSize( 0 ),
97 mnLastSizePos( 0 ),
98 mbInRec( false )
100 if( mnMaxRecSize == 0 )
101 mnMaxRecSize = (mrRoot.GetBiff() <= EXC_BIFF5) ? EXC_MAXRECSIZE_BIFF5 : EXC_MAXRECSIZE_BIFF8;
102 mnMaxContSize = mnMaxRecSize;
105 XclExpStream::~XclExpStream()
107 mrStrm.Flush();
110 void XclExpStream::StartRecord( sal_uInt16 nRecId, std::size_t nRecSize )
112 OSL_ENSURE( !mbInRec, "XclExpStream::StartRecord - another record still open" );
113 DisableEncryption();
114 mnMaxContSize = mnCurrMaxSize = mnMaxRecSize;
115 mnPredictSize = nRecSize;
116 mbInRec = true;
117 InitRecord( nRecId );
118 SetSliceSize( 0 );
119 EnableEncryption();
122 void XclExpStream::EndRecord()
124 OSL_ENSURE( mbInRec, "XclExpStream::EndRecord - no record open" );
125 DisableEncryption();
126 UpdateRecSize();
127 mrStrm.Seek( STREAM_SEEK_TO_END );
128 mbInRec = false;
131 void XclExpStream::SetSliceSize( sal_uInt16 nSize )
133 mnMaxSliceSize = nSize;
134 mnSliceSize = 0;
137 XclExpStream& XclExpStream::operator<<( sal_Int8 nValue )
139 PrepareWrite( 1 );
140 if (mbUseEncrypter && HasValidEncrypter())
141 mxEncrypter->Encrypt(mrStrm, nValue);
142 else
143 mrStrm.WriteSChar( nValue );
144 return *this;
147 XclExpStream& XclExpStream::operator<<( sal_uInt8 nValue )
149 PrepareWrite( 1 );
150 if (mbUseEncrypter && HasValidEncrypter())
151 mxEncrypter->Encrypt(mrStrm, nValue);
152 else
153 mrStrm.WriteUChar( nValue );
154 return *this;
157 XclExpStream& XclExpStream::operator<<( sal_Int16 nValue )
159 PrepareWrite( 2 );
160 if (mbUseEncrypter && HasValidEncrypter())
161 mxEncrypter->Encrypt(mrStrm, nValue);
162 else
163 mrStrm.WriteInt16( nValue );
164 return *this;
167 XclExpStream& XclExpStream::operator<<( sal_uInt16 nValue )
169 PrepareWrite( 2 );
170 if (mbUseEncrypter && HasValidEncrypter())
171 mxEncrypter->Encrypt(mrStrm, nValue);
172 else
173 mrStrm.WriteUInt16( nValue );
174 return *this;
177 XclExpStream& XclExpStream::operator<<( sal_Int32 nValue )
179 PrepareWrite( 4 );
180 if (mbUseEncrypter && HasValidEncrypter())
181 mxEncrypter->Encrypt(mrStrm, nValue);
182 else
183 mrStrm.WriteInt32( nValue );
184 return *this;
187 XclExpStream& XclExpStream::operator<<( sal_uInt32 nValue )
189 PrepareWrite( 4 );
190 if (mbUseEncrypter && HasValidEncrypter())
191 mxEncrypter->Encrypt(mrStrm, nValue);
192 else
193 mrStrm.WriteUInt32( nValue );
194 return *this;
197 XclExpStream& XclExpStream::operator<<( float fValue )
199 PrepareWrite( 4 );
200 if (mbUseEncrypter && HasValidEncrypter())
201 mxEncrypter->Encrypt(mrStrm, fValue);
202 else
203 mrStrm.WriteFloat( fValue );
204 return *this;
207 XclExpStream& XclExpStream::operator<<( double fValue )
209 PrepareWrite( 8 );
210 if (mbUseEncrypter && HasValidEncrypter())
211 mxEncrypter->Encrypt(mrStrm, fValue);
212 else
213 mrStrm.WriteDouble( fValue );
214 return *this;
217 std::size_t XclExpStream::Write( const void* pData, std::size_t nBytes )
219 std::size_t nRet = 0;
220 if( pData && (nBytes > 0) )
222 if( mbInRec )
224 const sal_uInt8* pBuffer = static_cast< const sal_uInt8* >( pData );
225 std::size_t nBytesLeft = nBytes;
226 bool bValid = true;
228 while( bValid && (nBytesLeft > 0) )
230 std::size_t nWriteLen = ::std::min< std::size_t >( PrepareWrite(), nBytesLeft );
231 std::size_t nWriteRet = nWriteLen;
232 if (mbUseEncrypter && HasValidEncrypter())
234 OSL_ENSURE(nWriteLen > 0, "XclExpStream::Write: write length is 0!");
235 vector<sal_uInt8> aBytes(nWriteLen);
236 memcpy(aBytes.data(), pBuffer, nWriteLen);
237 mxEncrypter->EncryptBytes(mrStrm, aBytes);
238 // TODO: How do I check if all the bytes have been successfully written ?
240 else
242 nWriteRet = mrStrm.WriteBytes(pBuffer, nWriteLen);
243 bValid = (nWriteLen == nWriteRet);
244 OSL_ENSURE( bValid, "XclExpStream::Write - stream write error" );
246 pBuffer += nWriteRet;
247 nRet += nWriteRet;
248 nBytesLeft -= nWriteRet;
249 UpdateSizeVars( nWriteRet );
252 else
253 nRet = mrStrm.WriteBytes(pData, nBytes);
255 return nRet;
258 void XclExpStream::WriteZeroBytes( std::size_t nBytes )
260 if( mbInRec )
262 std::size_t nBytesLeft = nBytes;
263 while( nBytesLeft > 0 )
265 std::size_t nWriteLen = ::std::min< std::size_t >( PrepareWrite(), nBytesLeft );
266 WriteRawZeroBytes( nWriteLen );
267 nBytesLeft -= nWriteLen;
268 UpdateSizeVars( nWriteLen );
271 else
272 WriteRawZeroBytes( nBytes );
275 void XclExpStream::WriteZeroBytesToRecord( std::size_t nBytes )
277 if (!mbInRec)
278 // not in record.
279 return;
281 for (std::size_t i = 0; i < nBytes; ++i)
282 *this << sal_uInt8(0)/*nZero*/;
285 void XclExpStream::CopyFromStream(SvStream& rInStrm, sal_uInt64 const nBytes)
287 sal_uInt64 const nRemaining(rInStrm.remainingSize());
288 sal_uInt64 nBytesLeft = ::std::min(nBytes, nRemaining);
289 if( nBytesLeft <= 0 )
290 return;
292 const std::size_t nMaxBuffer = 4096;
293 std::unique_ptr<sal_uInt8[]> pBuffer(
294 new sal_uInt8[ ::std::min<std::size_t>(nBytesLeft, nMaxBuffer) ]);
295 bool bValid = true;
297 while( bValid && (nBytesLeft > 0) )
299 std::size_t nWriteLen = ::std::min<std::size_t>(nBytesLeft, nMaxBuffer);
300 rInStrm.ReadBytes(pBuffer.get(), nWriteLen);
301 std::size_t nWriteRet = Write( pBuffer.get(), nWriteLen );
302 bValid = (nWriteLen == nWriteRet);
303 nBytesLeft -= nWriteRet;
307 void XclExpStream::WriteUnicodeBuffer( const ScfUInt16Vec& rBuffer, sal_uInt8 nFlags )
309 SetSliceSize( 0 );
310 nFlags &= EXC_STRF_16BIT; // repeat only 16bit flag
311 sal_uInt16 nCharLen = nFlags ? 2 : 1;
313 for( const auto& rItem : rBuffer )
315 if( mbInRec && (mnCurrSize + nCharLen > mnCurrMaxSize) )
317 StartContinue();
318 operator<<( nFlags );
320 if( nCharLen == 2 )
321 operator<<( rItem );
322 else
323 operator<<( static_cast< sal_uInt8 >( rItem ) );
327 // Xcl has an obscure sense of whether starting a new record or not,
328 // and crashes if it encounters the string header at the very end of a record.
329 // Thus we add 1 to give some room, seems like they do it that way but with another count (10?)
330 void XclExpStream::WriteByteString( const OString& rString )
332 SetSliceSize( 0 );
333 std::size_t nLen = ::std::min< std::size_t >( rString.getLength(), 0x00FF );
334 nLen = ::std::min< std::size_t >( nLen, 0xFF );
336 sal_uInt16 nLeft = PrepareWrite();
337 if( mbInRec && (nLeft <= 1) )
338 StartContinue();
340 operator<<( static_cast< sal_uInt8 >( nLen ) );
341 Write( rString.getStr(), nLen );
344 void XclExpStream::WriteCharBuffer( const ScfUInt8Vec& rBuffer )
346 SetSliceSize( 0 );
347 Write( rBuffer.data(), rBuffer.size() );
350 void XclExpStream::SetEncrypter( XclExpEncrypterRef const & xEncrypter )
352 mxEncrypter = xEncrypter;
355 bool XclExpStream::HasValidEncrypter() const
357 return mxEncrypter && mxEncrypter->IsValid();
360 void XclExpStream::EnableEncryption( bool bEnable )
362 mbUseEncrypter = bEnable && HasValidEncrypter();
365 void XclExpStream::DisableEncryption()
367 EnableEncryption(false);
370 void XclExpStream::SetSvStreamPos(sal_uInt64 const nPos)
372 OSL_ENSURE( !mbInRec, "XclExpStream::SetSvStreamPos - not allowed inside of a record" );
373 mbInRec ? 0 : mrStrm.Seek( nPos );
376 // private --------------------------------------------------------------------
378 void XclExpStream::InitRecord( sal_uInt16 nRecId )
380 mrStrm.Seek( STREAM_SEEK_TO_END );
381 mrStrm.WriteUInt16( nRecId );
383 mnLastSizePos = mrStrm.Tell();
384 mnHeaderSize = static_cast< sal_uInt16 >( ::std::min< std::size_t >( mnPredictSize, mnCurrMaxSize ) );
385 mrStrm.WriteUInt16( mnHeaderSize );
386 mnCurrSize = mnSliceSize = 0;
389 void XclExpStream::UpdateRecSize()
391 if( mnCurrSize != mnHeaderSize )
393 mrStrm.Seek( mnLastSizePos );
394 mrStrm.WriteUInt16( mnCurrSize );
398 void XclExpStream::UpdateSizeVars( std::size_t nSize )
400 OSL_ENSURE( mnCurrSize + nSize <= mnCurrMaxSize, "XclExpStream::UpdateSizeVars - record overwritten" );
401 mnCurrSize = mnCurrSize + static_cast< sal_uInt16 >( nSize );
403 if( mnMaxSliceSize > 0 )
405 OSL_ENSURE( mnSliceSize + nSize <= mnMaxSliceSize, "XclExpStream::UpdateSizeVars - slice overwritten" );
406 mnSliceSize = mnSliceSize + static_cast< sal_uInt16 >( nSize );
407 if( mnSliceSize >= mnMaxSliceSize )
408 mnSliceSize = 0;
412 void XclExpStream::StartContinue()
414 UpdateRecSize();
415 mnCurrMaxSize = mnMaxContSize;
416 mnPredictSize -= mnCurrSize;
417 InitRecord( EXC_ID_CONT );
420 void XclExpStream::PrepareWrite( sal_uInt16 nSize )
422 if( mbInRec )
424 if( (mnCurrSize + nSize > mnCurrMaxSize) ||
425 ((mnMaxSliceSize > 0) && (mnSliceSize == 0) && (mnCurrSize + mnMaxSliceSize > mnCurrMaxSize)) )
426 StartContinue();
427 UpdateSizeVars( nSize );
431 sal_uInt16 XclExpStream::PrepareWrite()
433 sal_uInt16 nRet = 0;
434 if( mbInRec )
436 if( (mnCurrSize >= mnCurrMaxSize) ||
437 ((mnMaxSliceSize > 0) && (mnSliceSize == 0) && (mnCurrSize + mnMaxSliceSize > mnCurrMaxSize)) )
438 StartContinue();
439 UpdateSizeVars( 0 );
441 nRet = (mnMaxSliceSize > 0) ? (mnMaxSliceSize - mnSliceSize) : (mnCurrMaxSize - mnCurrSize);
443 return nRet;
446 void XclExpStream::WriteRawZeroBytes( std::size_t nBytes )
448 const sal_uInt32 nData = 0;
449 std::size_t nBytesLeft = nBytes;
450 while( nBytesLeft >= sizeof( nData ) )
452 mrStrm.WriteUInt32( nData );
453 nBytesLeft -= sizeof( nData );
455 if( nBytesLeft )
456 mrStrm.WriteBytes(&nData, nBytesLeft);
459 XclExpBiff8Encrypter::XclExpBiff8Encrypter( const XclExpRoot& rRoot ) :
460 mnOldPos(STREAM_SEEK_TO_END),
461 mbValid(false)
463 Sequence< NamedValue > aEncryptionData = rRoot.GetEncryptionData();
464 if( !aEncryptionData.hasElements() )
465 // Empty password. Get the default biff8 password.
466 aEncryptionData = XclExpRoot::GenerateDefaultEncryptionData();
467 Init( aEncryptionData );
470 XclExpBiff8Encrypter::~XclExpBiff8Encrypter()
474 void XclExpBiff8Encrypter::GetSaltDigest( sal_uInt8 pnSaltDigest[16] ) const
476 if ( sizeof( mpnSaltDigest ) == 16 )
477 memcpy( pnSaltDigest, mpnSaltDigest, 16 );
480 void XclExpBiff8Encrypter::GetSalt( sal_uInt8 pnSalt[16] ) const
482 if ( sizeof( mpnSalt ) == 16 )
483 memcpy( pnSalt, mpnSalt, 16 );
486 void XclExpBiff8Encrypter::GetDocId( sal_uInt8 pnDocId[16] ) const
488 if ( sizeof( mpnDocId ) == 16 )
489 memcpy( pnDocId, mpnDocId, 16 );
492 void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt8 nData )
494 vector<sal_uInt8> aByte { nData };
495 EncryptBytes(rStrm, aByte);
498 void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt16 nData )
500 ::std::vector<sal_uInt8> pnBytes
502 o3tl::narrowing<sal_uInt8>(nData & 0xFF),
503 o3tl::narrowing<sal_uInt8>((nData >> 8) & 0xFF)
505 EncryptBytes(rStrm, pnBytes);
508 void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt32 nData )
510 ::std::vector<sal_uInt8> pnBytes
512 o3tl::narrowing<sal_uInt8>(nData & 0xFF),
513 o3tl::narrowing<sal_uInt8>((nData >> 8) & 0xFF),
514 o3tl::narrowing<sal_uInt8>((nData >> 16) & 0xFF),
515 o3tl::narrowing<sal_uInt8>((nData >> 24) & 0xFF)
517 EncryptBytes(rStrm, pnBytes);
520 void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, float fValue )
522 ::std::vector<sal_uInt8> pnBytes(4);
523 memcpy(pnBytes.data(), &fValue, 4);
524 EncryptBytes(rStrm, pnBytes);
527 void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, double fValue )
529 ::std::vector<sal_uInt8> pnBytes(8);
530 memcpy(pnBytes.data(), &fValue, 8);
531 EncryptBytes(rStrm, pnBytes);
534 void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int8 nData )
536 Encrypt(rStrm, static_cast<sal_uInt8>(nData));
539 void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int16 nData )
541 Encrypt(rStrm, static_cast<sal_uInt16>(nData));
544 void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int32 nData )
546 Encrypt(rStrm, static_cast<sal_uInt32>(nData));
549 void XclExpBiff8Encrypter::Init( const Sequence< NamedValue >& rEncryptionData )
551 mbValid = false;
553 if( !maCodec.InitCodec( rEncryptionData ) )
554 return;
556 maCodec.GetDocId( mpnDocId );
558 // generate the salt here
559 rtlRandomPool aRandomPool = rtl_random_createPool ();
560 rtl_random_getBytes( aRandomPool, mpnSalt, 16 );
561 rtl_random_destroyPool( aRandomPool );
563 memset( mpnSaltDigest, 0, sizeof( mpnSaltDigest ) );
565 // generate salt hash.
566 ::msfilter::MSCodec_Std97 aCodec;
567 aCodec.InitCodec( rEncryptionData );
568 aCodec.CreateSaltDigest( mpnSalt, mpnSaltDigest );
570 // verify to make sure it's in good shape.
571 mbValid = maCodec.VerifyKey( mpnSalt, mpnSaltDigest );
574 sal_uInt32 XclExpBiff8Encrypter::GetBlockPos( std::size_t nStrmPos )
576 return static_cast< sal_uInt32 >( nStrmPos / EXC_ENCR_BLOCKSIZE );
579 sal_uInt16 XclExpBiff8Encrypter::GetOffsetInBlock( std::size_t nStrmPos )
581 return static_cast< sal_uInt16 >( nStrmPos % EXC_ENCR_BLOCKSIZE );
584 void XclExpBiff8Encrypter::EncryptBytes( SvStream& rStrm, vector<sal_uInt8>& aBytes )
586 sal_uInt64 nStrmPos = rStrm.Tell();
587 sal_uInt16 nBlockOffset = GetOffsetInBlock(nStrmPos);
588 sal_uInt32 nBlockPos = GetBlockPos(nStrmPos);
590 #if DEBUG_XL_ENCRYPTION
591 fprintf(stdout, "XclExpBiff8Encrypter::EncryptBytes: stream pos = %ld offset in block = %d block pos = %ld\n",
592 nStrmPos, nBlockOffset, nBlockPos);
593 #endif
595 sal_uInt16 nSize = static_cast< sal_uInt16 >( aBytes.size() );
596 if (nSize == 0)
597 return;
599 #if DEBUG_XL_ENCRYPTION
600 fprintf(stdout, "RAW: ");
601 for (sal_uInt16 i = 0; i < nSize; ++i)
602 fprintf(stdout, "%2.2X ", aBytes[i]);
603 fprintf(stdout, "\n");
604 #endif
606 if (mnOldPos != nStrmPos)
608 sal_uInt16 nOldOffset = GetOffsetInBlock(mnOldPos);
609 sal_uInt32 nOldBlockPos = GetBlockPos(mnOldPos);
611 if ( (nBlockPos != nOldBlockPos) || (nBlockOffset < nOldOffset) )
613 maCodec.InitCipher(nBlockPos);
614 nOldOffset = 0;
617 if (nBlockOffset > nOldOffset)
618 maCodec.Skip(nBlockOffset - nOldOffset);
621 sal_uInt16 nBytesLeft = nSize;
622 sal_uInt16 nPos = 0;
623 while (nBytesLeft > 0)
625 sal_uInt16 nBlockLeft = EXC_ENCR_BLOCKSIZE - nBlockOffset;
626 sal_uInt16 nEncBytes = ::std::min(nBlockLeft, nBytesLeft);
628 bool bRet = maCodec.Encode(&aBytes[nPos], nEncBytes, &aBytes[nPos], nEncBytes);
629 OSL_ENSURE(bRet, "XclExpBiff8Encrypter::EncryptBytes: encryption failed!!");
631 std::size_t nRet = rStrm.WriteBytes(&aBytes[nPos], nEncBytes);
632 OSL_ENSURE(nRet == nEncBytes, "XclExpBiff8Encrypter::EncryptBytes: fail to write to stream!!");
634 nStrmPos = rStrm.Tell();
635 nBlockOffset = GetOffsetInBlock(nStrmPos);
636 nBlockPos = GetBlockPos(nStrmPos);
637 if (nBlockOffset == 0)
638 maCodec.InitCipher(nBlockPos);
640 nBytesLeft -= nEncBytes;
641 nPos += nEncBytes;
643 mnOldPos = nStrmPos;
646 static const char* lcl_GetErrorString( FormulaError nScErrCode )
648 sal_uInt8 nXclErrCode = XclTools::GetXclErrorCode( nScErrCode );
649 switch( nXclErrCode )
651 case EXC_ERR_NULL: return "#NULL!";
652 case EXC_ERR_DIV0: return "#DIV/0!";
653 case EXC_ERR_VALUE: return "#VALUE!";
654 case EXC_ERR_REF: return "#REF!";
655 case EXC_ERR_NAME: return "#NAME?";
656 case EXC_ERR_NUM: return "#NUM!";
657 case EXC_ERR_NA:
658 default: return "#N/A";
662 void XclXmlUtils::GetFormulaTypeAndValue( ScFormulaCell& rCell, const char*& rsType, OUString& rsValue )
664 sc::FormulaResultValue aResValue = rCell.GetResult();
666 switch (aResValue.meType)
668 case sc::FormulaResultValue::Error:
669 rsType = "e";
670 rsValue = ToOUString(lcl_GetErrorString(aResValue.mnError));
671 break;
672 case sc::FormulaResultValue::Value:
673 rsType = rCell.GetFormatType() == SvNumFormatType::LOGICAL
674 && (aResValue.mfValue == 0.0 || aResValue.mfValue == 1.0)
675 ? "b"
676 : "n";
677 rsValue = OUString::number(aResValue.mfValue);
678 break;
679 case sc::FormulaResultValue::String:
680 rsType = "str";
681 rsValue = rCell.GetString().getString();
682 break;
683 case sc::FormulaResultValue::Invalid:
684 default:
685 // TODO : double-check this to see if this is correct.
686 rsType = "inlineStr";
687 rsValue = rCell.GetString().getString();
691 OUString XclXmlUtils::GetStreamName( const char* sStreamDir, const char* sStream, sal_Int32 nId )
693 OUStringBuffer sBuf;
694 if( sStreamDir )
695 sBuf.appendAscii( sStreamDir );
696 sBuf.appendAscii( sStream );
697 if( nId )
698 sBuf.append( nId );
699 if( strstr(sStream, "vml") )
700 sBuf.append( ".vml" );
701 else
702 sBuf.append( ".xml" );
703 return sBuf.makeStringAndClear();
706 OString XclXmlUtils::ToOString( const Color& rColor )
708 char buf[9];
709 sprintf( buf, "%.2X%.2X%.2X%.2X", rColor.GetAlpha(), rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() );
710 buf[8] = '\0';
711 return OString(buf);
714 OStringBuffer& XclXmlUtils::ToOString( OStringBuffer& s, const ScAddress& rAddress )
716 rAddress.Format(s, ScRefFlags::VALID, nullptr, ScAddress::Details( FormulaGrammar::CONV_XL_A1));
717 return s;
720 OString XclXmlUtils::ToOString( const ScfUInt16Vec& rBuffer )
722 if(rBuffer.empty())
723 return OString();
725 const sal_uInt16* pBuffer = rBuffer.data();
726 return OString(
727 reinterpret_cast<sal_Unicode const *>(pBuffer), rBuffer.size(),
728 RTL_TEXTENCODING_UTF8);
731 OString XclXmlUtils::ToOString( const ScDocument& rDoc, const ScRange& rRange, bool bFullAddressNotation )
733 OUString sRange(rRange.Format( rDoc, ScRefFlags::VALID,
734 ScAddress::Details( FormulaGrammar::CONV_XL_A1 ),
735 bFullAddressNotation ) );
736 return sRange.toUtf8();
739 OString XclXmlUtils::ToOString( const ScDocument& rDoc, const ScRangeList& rRangeList )
741 OUString s;
742 rRangeList.Format(s, ScRefFlags::VALID, rDoc, FormulaGrammar::CONV_XL_OOX, ' ');
743 return s.toUtf8();
746 static ScAddress lcl_ToAddress( const XclAddress& rAddress )
748 return ScAddress( rAddress.mnCol, rAddress.mnRow, 0 );
751 OStringBuffer& XclXmlUtils::ToOString( OStringBuffer& s, const XclAddress& rAddress )
753 return ToOString( s, lcl_ToAddress( rAddress ));
756 OString XclXmlUtils::ToOString( const XclExpString& s )
758 OSL_ENSURE( !s.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" );
759 return ToOString( s.GetUnicodeBuffer() );
762 static ScRange lcl_ToRange( const XclRange& rRange )
764 ScRange aRange;
766 aRange.aStart = lcl_ToAddress( rRange.maFirst );
767 aRange.aEnd = lcl_ToAddress( rRange.maLast );
769 return aRange;
772 OString XclXmlUtils::ToOString( const ScDocument& rDoc, const XclRangeList& rRanges )
774 ScRangeList aRanges;
775 for( const auto& rRange : rRanges )
777 aRanges.push_back( lcl_ToRange( rRange ) );
779 return ToOString( rDoc, aRanges );
782 OUString XclXmlUtils::ToOUString( const char* s )
784 return OUString( s, static_cast<sal_Int32>(strlen( s )), RTL_TEXTENCODING_ASCII_US );
787 OUString XclXmlUtils::ToOUString( const ScfUInt16Vec& rBuf, sal_Int32 nStart, sal_Int32 nLength )
789 if( nLength == -1 || ( nLength > (static_cast<sal_Int32>(rBuf.size()) - nStart) ) )
790 nLength = (rBuf.size() - nStart);
792 return nLength > 0
793 ? OUString(
794 reinterpret_cast<sal_Unicode const *>(&rBuf[nStart]), nLength)
795 : OUString();
798 OUString XclXmlUtils::ToOUString(
799 sc::CompileFormulaContext& rCtx, const ScAddress& rAddress, const ScTokenArray* pTokenArray,
800 FormulaError nErrCode )
802 ScCompiler aCompiler( rCtx, rAddress, const_cast<ScTokenArray&>(*pTokenArray));
804 /* TODO: isn't this the same as passed in rCtx and thus superfluous? */
805 aCompiler.SetGrammar(FormulaGrammar::GRAM_OOXML);
807 sal_Int32 nLen = pTokenArray->GetLen();
808 OUStringBuffer aBuffer( nLen ? (nLen * 5) : 8 );
809 if (nLen)
810 aCompiler.CreateStringFromTokenArray( aBuffer );
811 else
813 if (nErrCode != FormulaError::NONE)
814 aCompiler.AppendErrorConstant( aBuffer, nErrCode);
815 else
817 // No code SHOULD be an "error cell", assert caller thought of that
818 // and it really is.
819 assert(!"No code and no error.");
823 return aBuffer.makeStringAndClear();
826 OUString XclXmlUtils::ToOUString( const XclExpString& s )
828 OSL_ENSURE( !s.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" );
829 return ToOUString( s.GetUnicodeBuffer() );
832 static void lcl_WriteValue( const sax_fastparser::FSHelperPtr& rStream, sal_Int32 nElement, const char* pValue )
834 if( !pValue )
835 return;
836 rStream->singleElement(nElement, XML_val, pValue);
839 static const char* lcl_GetUnderlineStyle( FontLineStyle eUnderline, bool& bHaveUnderline )
841 bHaveUnderline = true;
842 switch( eUnderline )
844 // OOXTODO: doubleAccounting, singleAccounting
845 // OOXTODO: what should be done with the other FontLineStyle values?
846 case LINESTYLE_SINGLE: return "single";
847 case LINESTYLE_DOUBLE: return "double";
848 case LINESTYLE_NONE:
849 default: bHaveUnderline = false; return "none";
853 static const char* lcl_ToVerticalAlignmentRun( SvxEscapement eEscapement, bool& bHaveAlignment )
855 bHaveAlignment = true;
856 switch( eEscapement )
858 case SvxEscapement::Superscript: return "superscript";
859 case SvxEscapement::Subscript: return "subscript";
860 case SvxEscapement::Off:
861 default: bHaveAlignment = false; return "baseline";
865 sax_fastparser::FSHelperPtr XclXmlUtils::WriteFontData( sax_fastparser::FSHelperPtr pStream, const XclFontData& rFontData, sal_Int32 nFontId )
867 bool bHaveUnderline, bHaveVertAlign;
868 const char* pUnderline = lcl_GetUnderlineStyle( rFontData.GetScUnderline(), bHaveUnderline );
869 const char* pVertAlign = lcl_ToVerticalAlignmentRun( rFontData.GetScEscapement(), bHaveVertAlign );
871 lcl_WriteValue( pStream, XML_b, rFontData.mnWeight > 400 ? ToPsz( true ) : nullptr );
872 lcl_WriteValue( pStream, XML_i, rFontData.mbItalic ? ToPsz( true ) : nullptr );
873 lcl_WriteValue( pStream, XML_strike, rFontData.mbStrikeout ? ToPsz( true ) : nullptr );
874 // OOXTODO: lcl_WriteValue( rStream, XML_condense, ); // mac compatibility setting
875 // OOXTODO: lcl_WriteValue( rStream, XML_extend, ); // compatibility setting
876 lcl_WriteValue( pStream, XML_outline, rFontData.mbOutline ? ToPsz( true ) : nullptr );
877 lcl_WriteValue( pStream, XML_shadow, rFontData.mbShadow ? ToPsz( true ) : nullptr );
878 lcl_WriteValue( pStream, XML_u, bHaveUnderline ? pUnderline : nullptr );
879 lcl_WriteValue( pStream, XML_vertAlign, bHaveVertAlign ? pVertAlign : nullptr );
880 lcl_WriteValue( pStream, XML_sz, OString::number( rFontData.mnHeight / 20.0 ).getStr() ); // Twips->Pt
881 if( rFontData.maColor != Color( ColorAlpha, 0, 0xFF, 0xFF, 0xFF ) )
882 pStream->singleElement( XML_color,
883 // OOXTODO: XML_auto, bool
884 // OOXTODO: XML_indexed, uint
885 XML_rgb, XclXmlUtils::ToOString(rFontData.maColor)
886 // OOXTODO: XML_theme, index into <clrScheme/>
887 // OOXTODO: XML_tint, double
889 lcl_WriteValue( pStream, nFontId, rFontData.maName.toUtf8().getStr() );
890 lcl_WriteValue( pStream, XML_family, OString::number( rFontData.mnFamily ).getStr() );
891 lcl_WriteValue( pStream, XML_charset, rFontData.mnCharSet != 0 ? OString::number( rFontData.mnCharSet ).getStr() : nullptr );
893 return pStream;
896 XclExpXmlStream::XclExpXmlStream( const uno::Reference< XComponentContext >& rCC, bool bExportVBA, bool bExportTemplate )
897 : XmlFilterBase( rCC ),
898 mpRoot( nullptr ),
899 mbExportVBA(bExportVBA),
900 mbExportTemplate(bExportTemplate)
904 XclExpXmlStream::~XclExpXmlStream()
906 assert(maStreams.empty() && "Forgotten PopStream()?");
909 sax_fastparser::FSHelperPtr& XclExpXmlStream::GetCurrentStream()
911 OSL_ENSURE( !maStreams.empty(), "XclExpXmlStream::GetCurrentStream - no current stream" );
912 return maStreams.top();
915 void XclExpXmlStream::PushStream( sax_fastparser::FSHelperPtr const & aStream )
917 maStreams.push( aStream );
920 void XclExpXmlStream::PopStream()
922 OSL_ENSURE( !maStreams.empty(), "XclExpXmlStream::PopStream - stack is empty!" );
923 maStreams.pop();
926 sax_fastparser::FSHelperPtr XclExpXmlStream::GetStreamForPath( const OUString& sPath )
928 if( maOpenedStreamMap.find( sPath ) == maOpenedStreamMap.end() )
929 return sax_fastparser::FSHelperPtr();
930 return maOpenedStreamMap[ sPath ].second;
933 void XclExpXmlStream::WriteAttribute(sal_Int32 nAttr, std::u16string_view sVal)
935 GetCurrentStream()->write(" ")->writeId(nAttr)->write("=\"")->writeEscaped(sVal)->write("\"");
938 sax_fastparser::FSHelperPtr XclExpXmlStream::CreateOutputStream (
939 const OUString& sFullStream,
940 std::u16string_view sRelativeStream,
941 const uno::Reference< XOutputStream >& xParentRelation,
942 const char* sContentType,
943 std::u16string_view sRelationshipType,
944 OUString* pRelationshipId )
946 OUString sRelationshipId;
947 if (xParentRelation.is())
948 sRelationshipId = addRelation( xParentRelation, OUString(sRelationshipType), sRelativeStream );
949 else
950 sRelationshipId = addRelation( OUString(sRelationshipType), sRelativeStream );
952 if( pRelationshipId )
953 *pRelationshipId = sRelationshipId;
955 sax_fastparser::FSHelperPtr p = openFragmentStreamWithSerializer( sFullStream, OUString::createFromAscii( sContentType ) );
957 maOpenedStreamMap[ sFullStream ] = std::make_pair( sRelationshipId, p );
959 return p;
962 bool XclExpXmlStream::importDocument() noexcept
964 return false;
967 oox::vml::Drawing* XclExpXmlStream::getVmlDrawing()
969 return nullptr;
972 const oox::drawingml::Theme* XclExpXmlStream::getCurrentTheme() const
974 return nullptr;
977 oox::drawingml::table::TableStyleListPtr XclExpXmlStream::getTableStyles()
979 return oox::drawingml::table::TableStyleListPtr();
982 oox::drawingml::chart::ChartConverter* XclExpXmlStream::getChartConverter()
984 // DO NOT CALL
985 return nullptr;
988 ScDocShell* XclExpXmlStream::getDocShell()
990 uno::Reference< XInterface > xModel( getModel(), UNO_QUERY );
992 ScModelObj *pObj = dynamic_cast < ScModelObj* >( xModel.get() );
994 if ( pObj )
995 return static_cast < ScDocShell* >( pObj->GetEmbeddedObject() );
997 return nullptr;
1000 bool XclExpXmlStream::exportDocument()
1002 ScDocShell* pShell = getDocShell();
1003 ScDocument& rDoc = pShell->GetDocument();
1004 ScRefreshTimerProtector aProt(rDoc.GetRefreshTimerControlAddress());
1006 const bool bValidateTabNames = officecfg::Office::Calc::Filter::Export::MS_Excel::TruncateLongSheetNames::get();
1007 std::vector<OUString> aOriginalTabNames;
1008 if (bValidateTabNames)
1010 validateTabNames(aOriginalTabNames);
1013 uno::Reference<task::XStatusIndicator> xStatusIndicator = getStatusIndicator();
1015 if (xStatusIndicator.is())
1016 xStatusIndicator->start(ScResId(STR_SAVE_DOC), 100);
1018 // NOTE: Don't use SotStorage or SvStream any more, and never call
1019 // SfxMedium::GetOutStream() anywhere in the xlsx export filter code!
1020 // Instead, write via XOutputStream instance.
1021 tools::SvRef<SotStorage> rStorage = static_cast<SotStorage*>(nullptr);
1022 drawingml::DrawingML::ResetMlCounters();
1024 XclExpRootData aData(
1025 EXC_BIFF8, *pShell->GetMedium (), rStorage, rDoc,
1026 msfilter::util::getBestTextEncodingFromLocale(
1027 Application::GetSettings().GetLanguageTag().getLocale()));
1028 aData.meOutput = EXC_OUTPUT_XML_2007;
1029 aData.maXclMaxPos.Set( EXC_MAXCOL_XML_2007, EXC_MAXROW_XML_2007, EXC_MAXTAB_XML_2007 );
1030 aData.maMaxPos.SetCol( ::std::min( aData.maScMaxPos.Col(), aData.maXclMaxPos.Col() ) );
1031 aData.maMaxPos.SetRow( ::std::min( aData.maScMaxPos.Row(), aData.maXclMaxPos.Row() ) );
1032 aData.maMaxPos.SetTab( ::std::min( aData.maScMaxPos.Tab(), aData.maXclMaxPos.Tab() ) );
1033 aData.mpCompileFormulaCxt = std::make_shared<sc::CompileFormulaContext>(rDoc);
1034 // set target path to get correct relative links to target document, not source
1035 INetURLObject aPath(getFileUrl());
1036 aData.maBasePath = OUString("file:///" + aPath.GetPath() + "\\").replace('\\', '/')
1037 // fix for Linux
1038 .replaceFirst("file:////", "file:///");
1040 XclExpRoot aRoot( aData );
1042 mpRoot = &aRoot;
1043 aRoot.GetOldRoot().pER = &aRoot;
1044 aRoot.GetOldRoot().eDateiTyp = Biff8;
1045 // Get the viewsettings before processing
1046 if( ScDocShell::GetViewData() )
1047 ScDocShell::GetViewData()->WriteExtOptions( mpRoot->GetExtDocOptions() );
1049 OUString const workbook = "xl/workbook.xml";
1050 const char* pWorkbookContentType = nullptr;
1051 if (mbExportVBA)
1053 if (mbExportTemplate)
1055 pWorkbookContentType = "application/vnd.ms-excel.template.macroEnabled.main+xml";
1057 else
1059 pWorkbookContentType = "application/vnd.ms-excel.sheet.macroEnabled.main+xml";
1062 else
1064 if (mbExportTemplate)
1066 pWorkbookContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml";
1068 else
1070 pWorkbookContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml";
1074 PushStream( CreateOutputStream( workbook, workbook,
1075 uno::Reference <XOutputStream>(),
1076 pWorkbookContentType,
1077 oox::getRelationship(Relationship::OFFICEDOCUMENT) ) );
1079 if (mbExportVBA)
1081 VbaExport aExport(getModel());
1082 if (aExport.containsVBAProject())
1084 SvMemoryStream aVbaStream(4096, 4096);
1085 tools::SvRef<SotStorage> pVBAStorage(new SotStorage(aVbaStream));
1086 aExport.exportVBA( pVBAStorage.get() );
1087 aVbaStream.Seek(0);
1088 css::uno::Reference<css::io::XInputStream> xVBAStream(
1089 new utl::OInputStreamWrapper(aVbaStream));
1090 css::uno::Reference<css::io::XOutputStream> xVBAOutput =
1091 openFragmentStream("xl/vbaProject.bin", "application/vnd.ms-office.vbaProject");
1092 comphelper::OStorageHelper::CopyInputToOutput(xVBAStream, xVBAOutput);
1094 addRelation(GetCurrentStream()->getOutputStream(), oox::getRelationship(Relationship::VBAPROJECT), u"vbaProject.bin");
1098 // destruct at the end of the block
1100 ExcDocument aDocRoot( aRoot );
1101 if (xStatusIndicator.is())
1102 xStatusIndicator->setValue(10);
1103 aDocRoot.ReadDoc();
1104 if (xStatusIndicator.is())
1105 xStatusIndicator->setValue(40);
1106 aDocRoot.WriteXml( *this );
1107 rDoc.GetExternalRefManager()->disableSkipUnusedFileIds();
1110 PopStream();
1111 // Free all FSHelperPtr, to flush data before committing storage
1112 maOpenedStreamMap.clear();
1114 commitStorage();
1116 if (bValidateTabNames)
1118 restoreTabNames(aOriginalTabNames);
1121 if (xStatusIndicator.is())
1122 xStatusIndicator->end();
1123 mpRoot = nullptr;
1124 return true;
1127 ::oox::ole::VbaProject* XclExpXmlStream::implCreateVbaProject() const
1129 return new ::oox::xls::ExcelVbaProject( getComponentContext(), uno::Reference< XSpreadsheetDocument >( getModel(), UNO_QUERY ) );
1132 OUString XclExpXmlStream::getImplementationName()
1134 return "TODO";
1137 void XclExpXmlStream::validateTabNames(std::vector<OUString>& aOriginalTabNames)
1139 const int MAX_TAB_NAME_LENGTH = 31;
1141 ScDocShell* pShell = getDocShell();
1142 ScDocument& rDoc = pShell->GetDocument();
1144 // get original names
1145 aOriginalTabNames.resize(rDoc.GetTableCount());
1146 for (SCTAB nTab=0; nTab < rDoc.GetTableCount(); nTab++)
1148 rDoc.GetName(nTab, aOriginalTabNames[nTab]);
1151 // new tab names
1152 std::vector<OUString> aNewTabNames;
1153 aNewTabNames.reserve(rDoc.GetTableCount());
1155 // check and rename
1156 for (SCTAB nTab=0; nTab < rDoc.GetTableCount(); nTab++)
1158 const OUString& rOriginalName = aOriginalTabNames[nTab];
1159 if (rOriginalName.getLength() > MAX_TAB_NAME_LENGTH)
1161 OUString aNewName;
1163 // let's try just truncate "<first 31 chars>"
1164 if (aNewName.isEmpty())
1166 aNewName = rOriginalName.copy(0, MAX_TAB_NAME_LENGTH);
1167 if (aNewTabNames.end() != std::find(aNewTabNames.begin(), aNewTabNames.end(), aNewName) ||
1168 aOriginalTabNames.end() != std::find(aOriginalTabNames.begin(), aOriginalTabNames.end(), aNewName))
1170 // was found => let's use another tab name
1171 aNewName.clear();
1175 // let's try "<first N chars>-XXX" template
1176 for (int digits=1; digits<10 && aNewName.isEmpty(); digits++)
1178 const int rangeStart = pow(10, digits - 1);
1179 const int rangeEnd = pow(10, digits);
1181 for (int i=rangeStart; i<rangeEnd && aNewName.isEmpty(); i++)
1183 aNewName = OUString::Concat(rOriginalName.subView(0, MAX_TAB_NAME_LENGTH - 1 - digits)) + "-" + OUString::number(i);
1184 if (aNewTabNames.end() != std::find(aNewTabNames.begin(), aNewTabNames.end(), aNewName) ||
1185 aOriginalTabNames.end() != std::find(aOriginalTabNames.begin(), aOriginalTabNames.end(), aNewName))
1187 // was found => let's use another tab name
1188 aNewName.clear();
1193 if (!aNewName.isEmpty())
1195 // new name was created => rename
1196 renameTab(nTab, aNewName);
1197 aNewTabNames.push_back(aNewName);
1199 else
1201 // default: do not rename
1202 aNewTabNames.push_back(rOriginalName);
1205 else
1207 // default: do not rename
1208 aNewTabNames.push_back(rOriginalName);
1213 void XclExpXmlStream::restoreTabNames(const std::vector<OUString>& aOriginalTabNames)
1215 ScDocShell* pShell = getDocShell();
1216 ScDocument& rDoc = pShell->GetDocument();
1218 for (SCTAB nTab=0; nTab < rDoc.GetTableCount(); nTab++)
1220 const OUString& rOriginalName = aOriginalTabNames[nTab];
1222 OUString rModifiedName;
1223 rDoc.GetName(nTab, rModifiedName);
1225 if (rOriginalName != rModifiedName)
1227 renameTab(nTab, rOriginalName);
1232 void XclExpXmlStream::renameTab(SCTAB aTab, OUString aNewName)
1234 ScDocShell* pShell = getDocShell();
1235 ScDocument& rDoc = pShell->GetDocument();
1237 bool bAutoCalcShellDisabled = rDoc.IsAutoCalcShellDisabled();
1238 bool bIdleEnabled = rDoc.IsIdleEnabled();
1240 rDoc.SetAutoCalcShellDisabled( true );
1241 rDoc.EnableIdle(false);
1243 if (rDoc.RenameTab(aTab, aNewName))
1245 SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScTablesChanged));
1248 rDoc.SetAutoCalcShellDisabled( bAutoCalcShellDisabled );
1249 rDoc.EnableIdle(bIdleEnabled);
1252 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */