1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
22 #include <xecontent.hxx>
26 #include <string_view>
28 #include <com/sun/star/frame/XModel.hpp>
29 #include <com/sun/star/sheet/XAreaLinks.hpp>
30 #include <com/sun/star/sheet/XAreaLink.hpp>
31 #include <com/sun/star/sheet/TableValidationVisibility.hpp>
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include <sfx2/objsh.hxx>
34 #include <tools/urlobj.hxx>
35 #include <formula/grammar.hxx>
36 #include <scitems.hxx>
37 #include <editeng/flditem.hxx>
38 #include <document.hxx>
40 #include <validat.hxx>
41 #include <unonames.hxx>
42 #include <convuno.hxx>
43 #include <rangenam.hxx>
44 #include <tokenarray.hxx>
45 #include <stlpool.hxx>
46 #include <patattr.hxx>
47 #include <fapihelper.hxx>
48 #include <xehelper.hxx>
49 #include <xestyle.hxx>
51 #include <xlcontent.hxx>
52 #include <xltools.hxx>
53 #include <xeformula.hxx>
55 #include <sal/log.hxx>
56 #include <oox/export/utils.hxx>
57 #include <oox/token/namespaces.hxx>
58 #include <oox/token/relationship.hxx>
59 #include <comphelper/string.hxx>
60 #include <o3tl/string_view.hxx>
62 using namespace ::oox
;
64 using ::com::sun::star::uno::Reference
;
65 using ::com::sun::star::uno::UNO_QUERY
;
66 using ::com::sun::star::table::CellRangeAddress
;
67 using ::com::sun::star::sheet::XAreaLinks
;
68 using ::com::sun::star::sheet::XAreaLink
;
70 // Shared string table ========================================================
74 /** A single string entry in the hash table. */
75 struct XclExpHashEntry
77 const XclExpString
* mpString
; /// Pointer to the string (no ownership).
78 sal_uInt32 mnSstIndex
; /// The SST index of this string.
79 explicit XclExpHashEntry( const XclExpString
* pString
, sal_uInt32 nSstIndex
) :
80 mpString( pString
), mnSstIndex( nSstIndex
) {}
83 /** Function object for strict weak ordering. */
84 struct XclExpHashEntrySWO
86 bool operator()( const XclExpHashEntry
& rLeft
, const XclExpHashEntry
& rRight
) const
87 { return *rLeft
.mpString
< *rRight
.mpString
; }
92 /** Implementation of the SST export.
93 @descr Stores all passed strings in a hash table and prevents repeated
94 insertion of equal strings. */
98 explicit XclExpSstImpl();
100 /** Inserts the passed string, if not already inserted, and returns the unique SST index. */
101 sal_uInt32
Insert( XclExpStringRef xString
);
103 /** Writes the complete SST and EXTSST records. */
104 void Save( XclExpStream
& rStrm
);
105 void SaveXml( XclExpXmlStream
& rStrm
);
108 typedef ::std::vector
< XclExpHashEntry
> XclExpHashVec
;
110 std::vector
< XclExpStringRef
> maStringVector
; /// List of unique strings (in SST ID order).
111 std::vector
< XclExpHashVec
>
112 maHashTab
; /// Hashed table that manages string pointers.
113 sal_uInt32 mnTotal
; /// Total count of strings (including doubles).
114 sal_uInt32 mnSize
; /// Size of the SST (count of unique strings).
117 const sal_uInt32 EXC_SST_HASHTABLE_SIZE
= 2048;
119 XclExpSstImpl::XclExpSstImpl() :
120 maHashTab( EXC_SST_HASHTABLE_SIZE
),
126 sal_uInt32
XclExpSstImpl::Insert( XclExpStringRef xString
)
128 OSL_ENSURE( xString
, "XclExpSstImpl::Insert - empty pointer not allowed" );
130 xString
.reset( new XclExpString
);
133 sal_uInt32 nSstIndex
= 0;
135 // calculate hash value in range [0,EXC_SST_HASHTABLE_SIZE)
136 sal_uInt16 nHash
= xString
->GetHash();
137 nHash
= (nHash
^ (nHash
/ EXC_SST_HASHTABLE_SIZE
)) % EXC_SST_HASHTABLE_SIZE
;
139 XclExpHashVec
& rVec
= maHashTab
[ nHash
];
140 XclExpHashEntry
aEntry( xString
.get(), mnSize
);
141 XclExpHashVec::iterator aIt
= ::std::lower_bound( rVec
.begin(), rVec
.end(), aEntry
, XclExpHashEntrySWO() );
142 if( (aIt
== rVec
.end()) || (*aIt
->mpString
!= *xString
) )
145 maStringVector
.push_back( xString
);
146 rVec
.insert( aIt
, aEntry
);
151 nSstIndex
= aIt
->mnSstIndex
;
157 void XclExpSstImpl::Save( XclExpStream
& rStrm
)
159 if( maStringVector
.empty() )
162 SvMemoryStream
aExtSst( 8192 );
164 sal_uInt32 nBucket
= mnSize
;
165 while( nBucket
> 0x0100 )
168 sal_uInt16 nPerBucket
= llimit_cast
< sal_uInt16
>( nBucket
, 8 );
169 sal_uInt16 nBucketIndex
= 0;
171 // *** write the SST record ***
173 rStrm
.StartRecord( EXC_ID_SST
, 8 );
175 rStrm
<< mnTotal
<< mnSize
;
176 for (auto const& elem
: maStringVector
)
180 // write bucket info before string to get correct record position
181 sal_uInt32 nStrmPos
= static_cast< sal_uInt32
>( rStrm
.GetSvStreamPos() );
182 sal_uInt16 nRecPos
= rStrm
.GetRawRecPos() + 4;
183 aExtSst
.WriteUInt32( nStrmPos
) // stream position
184 .WriteUInt16( nRecPos
) // position from start of SST or CONTINUE
185 .WriteUInt16( 0 ); // reserved
190 if( ++nBucketIndex
== nPerBucket
)
196 // *** write the EXTSST record ***
198 rStrm
.StartRecord( EXC_ID_EXTSST
, 0 );
201 rStrm
.SetSliceSize( 8 ); // size of one bucket info
202 aExtSst
.Seek( STREAM_SEEK_TO_BEGIN
);
203 rStrm
.CopyFromStream( aExtSst
);
208 void XclExpSstImpl::SaveXml( XclExpXmlStream
& rStrm
)
210 if( maStringVector
.empty() )
213 sax_fastparser::FSHelperPtr pSst
= rStrm
.CreateOutputStream(
214 u
"xl/sharedStrings.xml"_ustr
,
215 u
"sharedStrings.xml",
216 rStrm
.GetCurrentStream()->getOutputStream(),
217 "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
218 oox::getRelationship(Relationship::SHAREDSTRINGS
));
219 rStrm
.PushStream( pSst
);
221 pSst
->startElement( XML_sst
,
222 XML_xmlns
, rStrm
.getNamespaceURL(OOX_NS(xls
)),
223 XML_count
, OString::number(mnTotal
),
224 XML_uniqueCount
, OString::number(mnSize
) );
226 for (auto const& elem
: maStringVector
)
228 pSst
->startElement(XML_si
);
229 elem
->WriteXml( rStrm
);
230 pSst
->endElement( XML_si
);
233 pSst
->endElement( XML_sst
);
238 XclExpSst::XclExpSst() :
239 mxImpl( new XclExpSstImpl
)
243 XclExpSst::~XclExpSst()
247 sal_uInt32
XclExpSst::Insert( const XclExpStringRef
& xString
)
249 return mxImpl
->Insert( xString
);
252 void XclExpSst::Save( XclExpStream
& rStrm
)
254 mxImpl
->Save( rStrm
);
257 void XclExpSst::SaveXml( XclExpXmlStream
& rStrm
)
259 mxImpl
->SaveXml( rStrm
);
262 // Merged cells ===============================================================
264 XclExpMergedcells::XclExpMergedcells( const XclExpRoot
& rRoot
) :
269 void XclExpMergedcells::AppendRange( const ScRange
& rRange
, sal_uInt32 nBaseXFId
)
271 if( GetBiff() == EXC_BIFF8
)
273 maMergedRanges
.push_back( rRange
);
274 maBaseXFIds
.push_back( nBaseXFId
);
278 sal_uInt32
XclExpMergedcells::GetBaseXFId( const ScAddress
& rPos
) const
280 OSL_ENSURE( maBaseXFIds
.size() == maMergedRanges
.size(), "XclExpMergedcells::GetBaseXFId - invalid lists" );
281 ScfUInt32Vec::const_iterator aIt
= maBaseXFIds
.begin();
282 ScRangeList
& rNCRanges
= const_cast< ScRangeList
& >( maMergedRanges
);
283 for ( size_t i
= 0, nRanges
= rNCRanges
.size(); i
< nRanges
; ++i
, ++aIt
)
285 const ScRange
& rScRange
= rNCRanges
[ i
];
286 if( rScRange
.Contains( rPos
) )
289 return EXC_XFID_NOTFOUND
;
292 void XclExpMergedcells::Save( XclExpStream
& rStrm
)
294 if( GetBiff() != EXC_BIFF8
)
297 XclRangeList aXclRanges
;
298 GetAddressConverter().ConvertRangeList( aXclRanges
, maMergedRanges
, true );
299 size_t nFirstRange
= 0;
300 size_t nRemainingRanges
= aXclRanges
.size();
301 while( nRemainingRanges
> 0 )
303 size_t nRangeCount
= ::std::min
< size_t >( nRemainingRanges
, EXC_MERGEDCELLS_MAXCOUNT
);
304 rStrm
.StartRecord( EXC_ID_MERGEDCELLS
, 2 + 8 * nRangeCount
);
305 aXclRanges
.WriteSubList( rStrm
, nFirstRange
, nRangeCount
);
307 nFirstRange
+= nRangeCount
;
308 nRemainingRanges
-= nRangeCount
;
312 void XclExpMergedcells::SaveXml( XclExpXmlStream
& rStrm
)
314 size_t nCount
= maMergedRanges
.size();
317 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
318 rWorksheet
->startElement(XML_mergeCells
, XML_count
, OString::number(nCount
));
319 for( size_t i
= 0; i
< nCount
; ++i
)
321 const ScRange
& rRange
= maMergedRanges
[ i
];
322 rWorksheet
->singleElement(XML_mergeCell
, XML_ref
,
323 XclXmlUtils::ToOString(rStrm
.GetRoot().GetDoc(), rRange
));
325 rWorksheet
->endElement( XML_mergeCells
);
328 // Hyperlinks =================================================================
330 XclExpHyperlink::XclExpHyperlink( const XclExpRoot
& rRoot
, const SvxURLField
& rUrlField
, const ScAddress
& rScPos
) :
331 XclExpRecord( EXC_ID_HLINK
),
333 mxVarData( new SvMemoryStream
),
336 const OUString
& rUrl
= rUrlField
.GetURL();
337 const OUString
& rRepr
= rUrlField
.GetRepresentation();
338 INetURLObject
aUrlObj( rUrl
);
339 const INetProtocol eProtocol
= aUrlObj
.GetProtocol();
340 bool bWithRepr
= !rRepr
.isEmpty();
341 XclExpStream
aXclStrm( *mxVarData
, rRoot
); // using in raw write mode.
346 XclExpString
aDescr( rRepr
, XclStrFlags::ForceUnicode
, 255 );
347 aXclStrm
<< sal_uInt32( aDescr
.Len() + 1 ); // string length + 1 trailing zero word
348 aDescr
.WriteBuffer( aXclStrm
); // NO flags
349 aXclStrm
<< sal_uInt16( 0 );
351 mnFlags
|= EXC_HLINK_DESCR
;
356 if( eProtocol
== INetProtocol::File
|| eProtocol
== INetProtocol::Smb
)
361 BuildFileName(nLevel
, bRel
, rUrl
, rRoot
, rRoot
.GetOutput() == EXC_OUTPUT_XML_2007
));
363 if( eProtocol
== INetProtocol::Smb
)
365 // #n382718# (and #n261623#) Convert smb notation to '\\'
366 aFileName
= aUrlObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
367 aFileName
= aFileName
.copy(4); // skip the 'smb:' part
368 aFileName
= aFileName
.replace('/', '\\');
372 mnFlags
|= EXC_HLINK_ABS
;
373 mnFlags
|= EXC_HLINK_BODY
;
375 OString
aAsciiLink(OUStringToOString(aFileName
,
376 rRoot
.GetTextEncoding()));
377 XclExpString
aLink( aFileName
, XclStrFlags::ForceUnicode
, 255 );
378 aXclStrm
<< XclTools::maGuidFileMoniker
380 << sal_uInt32( aAsciiLink
.getLength() + 1 ); // string length + 1 trailing zero byte
381 aXclStrm
.Write( aAsciiLink
.getStr(), aAsciiLink
.getLength() );
382 aXclStrm
<< sal_uInt8( 0 )
383 << sal_uInt32( 0xDEADFFFF );
384 aXclStrm
.WriteZeroBytes( 20 );
385 aXclStrm
<< sal_uInt32( aLink
.GetBufferSize() + 6 )
386 << sal_uInt32( aLink
.GetBufferSize() ) // byte count, not string length
387 << sal_uInt16( 0x0003 );
388 aLink
.WriteBuffer( aXclStrm
); // NO flags
390 if (m_Repr
.isEmpty())
393 msTarget
= XclXmlUtils::ToOUString( aLink
);
397 for( int i
= 0; i
< nLevel
; ++i
)
398 msTarget
= "../" + msTarget
;
400 else if (rRoot
.GetOutput() != EXC_OUTPUT_XML_2007
)
402 // xls expects the file:/// part appended ( or at least
403 // ms2007 does, ms2010 is more tolerant )
404 msTarget
= "file:///" + msTarget
;
407 else if( eProtocol
!= INetProtocol::NotValid
)
409 XclExpString
aUrl( aUrlObj
.GetURLNoMark(), XclStrFlags::ForceUnicode
, 255 );
410 aXclStrm
<< XclTools::maGuidUrlMoniker
411 << sal_uInt32( aUrl
.GetBufferSize() + 2 ); // byte count + 1 trailing zero word
412 aUrl
.WriteBuffer( aXclStrm
); // NO flags
413 aXclStrm
<< sal_uInt16( 0 );
415 mnFlags
|= EXC_HLINK_BODY
| EXC_HLINK_ABS
;
416 if (m_Repr
.isEmpty())
419 msTarget
= XclXmlUtils::ToOUString( aUrl
);
421 else if( !rUrl
.isEmpty() && rUrl
[0] == '#' ) // hack for #89066#
423 OUString
aTextMark( rUrl
.copy( 1 ) );
425 sal_Int32 nSepPos
= aTextMark
.lastIndexOf( '!' );
426 sal_Int32 nPointPos
= aTextMark
.lastIndexOf( '.' );
427 // last dot is the separator, if there is no ! after it
428 if(nSepPos
< nPointPos
)
431 aTextMark
= aTextMark
.replaceAt( nSepPos
, 1, u
"!" );
436 std::u16string_view
aSheetName(aTextMark
.subView(0, nSepPos
));
438 if (aSheetName
.find(' ') != std::u16string_view::npos
&& aSheetName
[0] != '\'')
440 aTextMark
= "'" + aTextMark
.replaceAt(nSepPos
, 0, u
"'");
446 if (rRoot
.GetDoc().GetTable(aTextMark
, nTab
))
447 aTextMark
+= "!A1"; // tdf#143220 link to sheet not valid without cell reference
450 mxTextMark
.reset( new XclExpString( aTextMark
, XclStrFlags::ForceUnicode
, 255 ) );
454 if( !mxTextMark
&& aUrlObj
.HasMark() )
455 mxTextMark
.reset( new XclExpString( aUrlObj
.GetMark(), XclStrFlags::ForceUnicode
, 255 ) );
459 aXclStrm
<< sal_uInt32( mxTextMark
->Len() + 1 ); // string length + 1 trailing zero word
460 mxTextMark
->WriteBuffer( aXclStrm
); // NO flags
461 aXclStrm
<< sal_uInt16( 0 );
463 mnFlags
|= EXC_HLINK_MARK
;
465 OUString location
= XclXmlUtils::ToOUString(*mxTextMark
);
466 if (!location
.isEmpty() && msTarget
.endsWith(Concat2View("#" + location
)))
467 msTarget
= msTarget
.copy(0, msTarget
.getLength() - location
.getLength() - 1);
470 SetRecSize( 32 + mxVarData
->Tell() );
473 XclExpHyperlink::~XclExpHyperlink()
477 OUString
XclExpHyperlink::BuildFileName(
478 sal_uInt16
& rnLevel
, bool& rbRel
, const OUString
& rUrl
, const XclExpRoot
& rRoot
, bool bEncoded
)
480 INetURLObject
aURLObject( rUrl
);
481 OUString
aDosName(bEncoded
? aURLObject
.GetMainURL(INetURLObject::DecodeMechanism::ToIUri
)
482 : aURLObject
.getFSysPath(FSysStyle::Dos
));
484 rbRel
= rRoot
.IsRelUrl();
488 // try to convert to relative file name
489 OUString
aTmpName( aDosName
);
490 aDosName
= INetURLObject::GetRelURL( rRoot
.GetBasePath(), rUrl
,
491 INetURLObject::EncodeMechanism::WasEncoded
,
492 (bEncoded
? INetURLObject::DecodeMechanism::ToIUri
: INetURLObject::DecodeMechanism::WithCharset
));
494 if (aDosName
.startsWith(INET_FILE_SCHEME
))
496 // not converted to rel -> back to old, return absolute flag
500 else if (aDosName
.startsWith("./"))
502 aDosName
= aDosName
.copy(2);
506 while (aDosName
.startsWith("../"))
508 aDosName
= aDosName
.copy(3);
516 void XclExpHyperlink::WriteBody( XclExpStream
& rStrm
)
518 sal_uInt16 nXclCol
= static_cast< sal_uInt16
>( maScPos
.Col() );
519 sal_uInt16 nXclRow
= static_cast< sal_uInt16
>( maScPos
.Row() );
520 rStrm
<< nXclRow
<< nXclRow
<< nXclCol
<< nXclCol
;
521 WriteEmbeddedData( rStrm
);
524 void XclExpHyperlink::WriteEmbeddedData( XclExpStream
& rStrm
)
526 rStrm
<< XclTools::maGuidStdLink
530 mxVarData
->Seek( STREAM_SEEK_TO_BEGIN
);
531 rStrm
.CopyFromStream( *mxVarData
);
534 void XclExpHyperlink::SaveXml( XclExpXmlStream
& rStrm
)
536 OUString sId
= !msTarget
.isEmpty() ? rStrm
.addRelation( rStrm
.GetCurrentStream()->getOutputStream(),
537 oox::getRelationship(Relationship::HYPERLINK
),
538 msTarget
, true ) : OUString();
539 std::optional
<OString
> sTextMark
;
541 sTextMark
= XclXmlUtils::ToOString(*mxTextMark
);
542 rStrm
.GetCurrentStream()->singleElement( XML_hyperlink
,
543 XML_ref
, XclXmlUtils::ToOString(rStrm
.GetRoot().GetDoc(), ScRange(maScPos
)),
544 FSNS( XML_r
, XML_id
), sax_fastparser::UseIf(sId
, !sId
.isEmpty()),
545 XML_location
, sTextMark
,
546 // OOXTODO: XML_tooltip, from record HLinkTooltip 800h wzTooltip
547 XML_display
, m_Repr
);
550 // Label ranges ===============================================================
552 XclExpLabelranges::XclExpLabelranges( const XclExpRoot
& rRoot
) :
555 SCTAB nScTab
= GetCurrScTab();
557 FillRangeList( maRowRanges
, rRoot
.GetDoc().GetRowNameRangesRef(), nScTab
);
558 // row labels only over 1 column (restriction of Excel97/2000/XP)
559 for ( size_t i
= 0, nRanges
= maRowRanges
.size(); i
< nRanges
; ++i
)
561 ScRange
& rScRange
= maRowRanges
[ i
];
562 if( rScRange
.aStart
.Col() != rScRange
.aEnd
.Col() )
563 rScRange
.aEnd
.SetCol( rScRange
.aStart
.Col() );
566 FillRangeList( maColRanges
, rRoot
.GetDoc().GetColNameRangesRef(), nScTab
);
569 void XclExpLabelranges::FillRangeList( ScRangeList
& rScRanges
,
570 const ScRangePairListRef
& xLabelRangesRef
, SCTAB nScTab
)
572 for ( size_t i
= 0, nPairs
= xLabelRangesRef
->size(); i
< nPairs
; ++i
)
574 const ScRangePair
& rRangePair
= (*xLabelRangesRef
)[i
];
575 const ScRange
& rScRange
= rRangePair
.GetRange( 0 );
576 if( rScRange
.aStart
.Tab() == nScTab
)
577 rScRanges
.push_back( rScRange
);
581 void XclExpLabelranges::Save( XclExpStream
& rStrm
)
583 XclExpAddressConverter
& rAddrConv
= GetAddressConverter();
584 XclRangeList aRowXclRanges
, aColXclRanges
;
585 rAddrConv
.ConvertRangeList( aRowXclRanges
, maRowRanges
, false );
586 rAddrConv
.ConvertRangeList( aColXclRanges
, maColRanges
, false );
587 if( !aRowXclRanges
.empty() || !aColXclRanges
.empty() )
589 rStrm
.StartRecord( EXC_ID_LABELRANGES
, 4 + 8 * (aRowXclRanges
.size() + aColXclRanges
.size()) );
590 rStrm
<< aRowXclRanges
<< aColXclRanges
;
595 // Conditional formatting ====================================================
597 /** Represents a CF record that contains one condition of a conditional format. */
598 class XclExpCFImpl
: protected XclExpRoot
601 explicit XclExpCFImpl( const XclExpRoot
& rRoot
, const ScCondFormatEntry
& rFormatEntry
, sal_Int32 nPriority
, ScAddress aOrigin
);
603 /** Writes the body of the CF record. */
604 void WriteBody( XclExpStream
& rStrm
);
605 void SaveXml( XclExpXmlStream
& rStrm
);
608 const ScCondFormatEntry
& mrFormatEntry
; /// Calc conditional format entry.
609 ScAddress maOrigin
; /// Top left cell of the combined range
610 XclFontData maFontData
; /// Font formatting attributes.
611 XclExpCellBorder maBorder
; /// Border formatting attributes.
612 XclExpCellArea maArea
; /// Pattern formatting attributes.
613 XclTokenArrayRef mxTokArr1
; /// Formula for first condition.
614 XclTokenArrayRef mxTokArr2
; /// Formula for second condition.
615 sal_uInt32 mnFontColorId
; /// Font color ID.
616 sal_uInt8 mnType
; /// Type of the condition (cell/formula).
617 sal_uInt8 mnOperator
; /// Comparison operator for cell type.
618 sal_Int32 mnPriority
; /// Priority of this entry; needed for oox export
619 bool mbFontUsed
; /// true = Any font attribute used.
620 bool mbHeightUsed
; /// true = Font height used.
621 bool mbWeightUsed
; /// true = Font weight used.
622 bool mbColorUsed
; /// true = Font color used.
623 bool mbUnderlUsed
; /// true = Font underline type used.
624 bool mbItalicUsed
; /// true = Font posture used.
625 bool mbStrikeUsed
; /// true = Font strikeout used.
626 bool mbBorderUsed
; /// true = Border attribute used.
627 bool mbPattUsed
; /// true = Pattern attribute used.
631 XclExpCFImpl::XclExpCFImpl( const XclExpRoot
& rRoot
, const ScCondFormatEntry
& rFormatEntry
, sal_Int32 nPriority
, ScAddress aOrigin
) :
633 mrFormatEntry( rFormatEntry
),
636 mnType( EXC_CF_TYPE_CELL
),
637 mnOperator( EXC_CF_CMP_NONE
),
638 mnPriority( nPriority
),
640 mbHeightUsed( false ),
641 mbWeightUsed( false ),
642 mbColorUsed( false ),
643 mbUnderlUsed( false ),
644 mbItalicUsed( false ),
645 mbStrikeUsed( false ),
646 mbBorderUsed( false ),
650 // Set correct tab for maOrigin from GetValidSrcPos() of the format-entry.
651 ScAddress aValidSrcPos
= mrFormatEntry
.GetValidSrcPos();
652 maOrigin
.SetTab(aValidSrcPos
.Tab());
654 /* Get formatting attributes here, and not in WriteBody(). This is needed to
655 correctly insert all colors into the palette. */
657 if( SfxStyleSheetBase
* pStyleSheet
= GetDoc().GetStyleSheetPool()->Find( mrFormatEntry
.GetStyle(), SfxStyleFamily::Para
) )
659 const SfxItemSet
& rItemSet
= pStyleSheet
->GetItemSet();
662 mbHeightUsed
= ScfTools::CheckItem( rItemSet
, ATTR_FONT_HEIGHT
, true );
663 mbWeightUsed
= ScfTools::CheckItem( rItemSet
, ATTR_FONT_WEIGHT
, true );
664 mbColorUsed
= ScfTools::CheckItem( rItemSet
, ATTR_FONT_COLOR
, true );
665 mbUnderlUsed
= ScfTools::CheckItem( rItemSet
, ATTR_FONT_UNDERLINE
, true );
666 mbItalicUsed
= ScfTools::CheckItem( rItemSet
, ATTR_FONT_POSTURE
, true );
667 mbStrikeUsed
= ScfTools::CheckItem( rItemSet
, ATTR_FONT_CROSSEDOUT
, true );
668 mbFontUsed
= mbHeightUsed
|| mbWeightUsed
|| mbColorUsed
|| mbUnderlUsed
|| mbItalicUsed
|| mbStrikeUsed
;
672 model::ComplexColor aComplexColor
;
673 ScPatternAttr::fillFontOnly(aFont
, rItemSet
);
674 ScPatternAttr::fillColor(aComplexColor
, rItemSet
, ScAutoFontColorMode::Raw
);
675 maFontData
.FillFromVclFont(aFont
, aComplexColor
);
676 mnFontColorId
= GetPalette().InsertColor(maFontData
.maComplexColor
.getFinalColor(), EXC_COLOR_CELLTEXT
);
680 mbBorderUsed
= ScfTools::CheckItem( rItemSet
, ATTR_BORDER
, true );
682 maBorder
.FillFromItemSet( rItemSet
, GetPalette(), GetBiff() );
685 mbPattUsed
= ScfTools::CheckItem( rItemSet
, ATTR_BACKGROUND
, true );
687 maArea
.FillFromItemSet( rItemSet
, GetPalette(), true );
690 // *** mode and comparison operator ***
692 switch( rFormatEntry
.GetOperation() )
694 case ScConditionMode::NONE
:
695 mnType
= EXC_CF_TYPE_NONE
;
697 case ScConditionMode::Between
:
698 mnOperator
= EXC_CF_CMP_BETWEEN
;
701 case ScConditionMode::NotBetween
:
702 mnOperator
= EXC_CF_CMP_NOT_BETWEEN
;
705 case ScConditionMode::Equal
:
706 mnOperator
= EXC_CF_CMP_EQUAL
;
708 case ScConditionMode::NotEqual
:
709 mnOperator
= EXC_CF_CMP_NOT_EQUAL
;
711 case ScConditionMode::Greater
:
712 mnOperator
= EXC_CF_CMP_GREATER
;
714 case ScConditionMode::Less
:
715 mnOperator
= EXC_CF_CMP_LESS
;
717 case ScConditionMode::EqGreater
:
718 mnOperator
= EXC_CF_CMP_GREATER_EQUAL
;
720 case ScConditionMode::EqLess
:
721 mnOperator
= EXC_CF_CMP_LESS_EQUAL
;
723 case ScConditionMode::Direct
:
724 mnType
= EXC_CF_TYPE_FMLA
;
727 mnType
= EXC_CF_TYPE_NONE
;
728 OSL_FAIL( "XclExpCF::WriteBody - unknown condition type" );
732 void XclExpCFImpl::WriteBody( XclExpStream
& rStrm
)
737 XclExpFormulaCompiler
& rFmlaComp
= GetFormulaCompiler();
739 std::unique_ptr
< ScTokenArray
> xScTokArr( mrFormatEntry
.CreateFlatCopiedTokenArray( 0 ) );
740 mxTokArr1
= rFmlaComp
.CreateFormula( EXC_FMLATYPE_CONDFMT
, *xScTokArr
);
744 xScTokArr
= mrFormatEntry
.CreateFlatCopiedTokenArray( 1 );
745 mxTokArr2
= rFmlaComp
.CreateFormula( EXC_FMLATYPE_CONDFMT
, *xScTokArr
);
748 // *** mode and comparison operator ***
750 rStrm
<< mnType
<< mnOperator
;
752 // *** formula sizes ***
754 sal_uInt16 nFmlaSize1
= mxTokArr1
? mxTokArr1
->GetSize() : 0;
755 sal_uInt16 nFmlaSize2
= mxTokArr2
? mxTokArr2
->GetSize() : 0;
756 rStrm
<< nFmlaSize1
<< nFmlaSize2
;
758 // *** formatting blocks ***
760 if( mbFontUsed
|| mbBorderUsed
|| mbPattUsed
)
762 sal_uInt32 nFlags
= EXC_CF_ALLDEFAULT
;
764 ::set_flag( nFlags
, EXC_CF_BLOCK_FONT
, mbFontUsed
);
765 ::set_flag( nFlags
, EXC_CF_BLOCK_BORDER
, mbBorderUsed
);
766 ::set_flag( nFlags
, EXC_CF_BLOCK_AREA
, mbPattUsed
);
768 // attributes used -> set flags to 0.
769 ::set_flag( nFlags
, EXC_CF_BORDER_ALL
, !mbBorderUsed
);
770 ::set_flag( nFlags
, EXC_CF_AREA_ALL
, !mbPattUsed
);
772 rStrm
<< nFlags
<< sal_uInt16( 0 );
776 // font height, 0xFFFFFFFF indicates unused
777 sal_uInt32 nHeight
= mbHeightUsed
? maFontData
.mnHeight
: 0xFFFFFFFF;
778 // font style: italic and strikeout
779 sal_uInt32 nStyle
= 0;
780 ::set_flag( nStyle
, EXC_CF_FONT_STYLE
, maFontData
.mbItalic
);
781 ::set_flag( nStyle
, EXC_CF_FONT_STRIKEOUT
, maFontData
.mbStrikeout
);
782 // font color, 0xFFFFFFFF indicates unused
783 sal_uInt32 nColor
= mbColorUsed
? GetPalette().GetColorIndex( mnFontColorId
) : 0xFFFFFFFF;
784 // font used flags for italic, weight, and strikeout -> 0 = used, 1 = default
785 sal_uInt32 nFontFlags1
= EXC_CF_FONT_ALLDEFAULT
;
786 ::set_flag( nFontFlags1
, EXC_CF_FONT_STYLE
, !(mbItalicUsed
|| mbWeightUsed
) );
787 ::set_flag( nFontFlags1
, EXC_CF_FONT_STRIKEOUT
, !mbStrikeUsed
);
788 // font used flag for underline -> 0 = used, 1 = default
789 sal_uInt32 nFontFlags3
= mbUnderlUsed
? 0 : EXC_CF_FONT_UNDERL
;
791 rStrm
.WriteZeroBytesToRecord( 64 );
794 << maFontData
.mnWeight
796 << maFontData
.mnUnderline
;
797 rStrm
.WriteZeroBytesToRecord( 3 );
801 << EXC_CF_FONT_ESCAPEM
// escapement never used -> set the flag
803 rStrm
.WriteZeroBytesToRecord( 16 );
804 rStrm
<< sal_uInt16( 1 ); // must be 1
809 sal_uInt16 nLineStyle
= 0;
810 sal_uInt32 nLineColor
= 0;
811 maBorder
.SetFinalColors( GetPalette() );
812 maBorder
.FillToCF8( nLineStyle
, nLineColor
);
813 rStrm
<< nLineStyle
<< nLineColor
<< sal_uInt16( 0 );
818 sal_uInt16 nPattern
= 0, nColor
= 0;
819 maArea
.SetFinalColors( GetPalette() );
820 maArea
.FillToCF8( nPattern
, nColor
);
821 rStrm
<< nPattern
<< nColor
;
826 // no data blocks at all
827 rStrm
<< sal_uInt32( 0 ) << sal_uInt16( 0 );
833 mxTokArr1
->WriteArray( rStrm
);
835 mxTokArr2
->WriteArray( rStrm
);
840 const char* GetOperatorString(ScConditionMode eMode
, bool& bFrmla2
)
842 const char *pRet
= nullptr;
845 case ScConditionMode::Equal
:
848 case ScConditionMode::Less
:
851 case ScConditionMode::Greater
:
852 pRet
= "greaterThan";
854 case ScConditionMode::EqLess
:
855 pRet
= "lessThanOrEqual";
857 case ScConditionMode::EqGreater
:
858 pRet
= "greaterThanOrEqual";
860 case ScConditionMode::NotEqual
:
863 case ScConditionMode::Between
:
867 case ScConditionMode::NotBetween
:
871 case ScConditionMode::Duplicate
:
872 case ScConditionMode::NotDuplicate
:
875 case ScConditionMode::BeginsWith
:
878 case ScConditionMode::EndsWith
:
881 case ScConditionMode::ContainsText
:
882 pRet
= "containsText";
884 case ScConditionMode::NotContainsText
:
885 pRet
= "notContains";
887 case ScConditionMode::Direct
:
889 case ScConditionMode::NONE
:
896 const char* GetTypeString(ScConditionMode eMode
)
900 case ScConditionMode::Direct
:
902 case ScConditionMode::Top10
:
903 case ScConditionMode::TopPercent
:
904 case ScConditionMode::Bottom10
:
905 case ScConditionMode::BottomPercent
:
907 case ScConditionMode::AboveAverage
:
908 case ScConditionMode::BelowAverage
:
909 case ScConditionMode::AboveEqualAverage
:
910 case ScConditionMode::BelowEqualAverage
:
911 return "aboveAverage";
912 case ScConditionMode::NotDuplicate
:
913 return "uniqueValues";
914 case ScConditionMode::Duplicate
:
915 return "duplicateValues";
916 case ScConditionMode::Error
:
917 return "containsErrors";
918 case ScConditionMode::NoError
:
919 return "notContainsErrors";
920 case ScConditionMode::BeginsWith
:
922 case ScConditionMode::EndsWith
:
924 case ScConditionMode::ContainsText
:
925 return "containsText";
926 case ScConditionMode::NotContainsText
:
927 return "notContainsText";
933 bool IsTopBottomRule(ScConditionMode eMode
)
937 case ScConditionMode::Top10
:
938 case ScConditionMode::Bottom10
:
939 case ScConditionMode::TopPercent
:
940 case ScConditionMode::BottomPercent
:
949 bool IsTextRule(ScConditionMode eMode
)
953 case ScConditionMode::BeginsWith
:
954 case ScConditionMode::EndsWith
:
955 case ScConditionMode::ContainsText
:
956 case ScConditionMode::NotContainsText
:
965 bool RequiresFormula(ScConditionMode eMode
)
967 if (IsTopBottomRule(eMode
))
969 else if (IsTextRule(eMode
))
974 case ScConditionMode::NoError
:
975 case ScConditionMode::Error
:
976 case ScConditionMode::Duplicate
:
977 case ScConditionMode::NotDuplicate
:
986 bool RequiresFixedFormula(ScConditionMode eMode
)
990 case ScConditionMode::NoError
:
991 case ScConditionMode::Error
:
992 case ScConditionMode::BeginsWith
:
993 case ScConditionMode::EndsWith
:
994 case ScConditionMode::ContainsText
:
995 case ScConditionMode::NotContainsText
:
1004 OString
GetFixedFormula(ScConditionMode eMode
, const ScAddress
& rAddress
, std::string_view rText
)
1006 OStringBuffer aBuffer
;
1007 XclXmlUtils::ToOString(aBuffer
, rAddress
);
1008 OString aPos
= aBuffer
.makeStringAndClear();
1011 case ScConditionMode::Error
:
1012 return OString("ISERROR(" + aPos
+ ")") ;
1013 case ScConditionMode::NoError
:
1014 return OString("NOT(ISERROR(" + aPos
+ "))") ;
1015 case ScConditionMode::BeginsWith
:
1016 return OString("LEFT(" + aPos
+ ",LEN(\"" + rText
+ "\"))=\"" + rText
+ "\"");
1017 case ScConditionMode::EndsWith
:
1018 return OString("RIGHT(" + aPos
+",LEN(\"" + rText
+ "\"))=\"" + rText
+ "\"");
1019 case ScConditionMode::ContainsText
:
1020 return OString(OString::Concat("NOT(ISERROR(SEARCH(\"") + rText
+ "\"," + aPos
+ ")))");
1021 case ScConditionMode::NotContainsText
:
1022 return OString(OString::Concat("ISERROR(SEARCH(\"") + rText
+ "\"," + aPos
+ "))");
1032 void XclExpCFImpl::SaveXml( XclExpXmlStream
& rStrm
)
1034 bool bFmla2
= false;
1035 ScConditionMode eOperation
= mrFormatEntry
.GetOperation();
1036 bool bAboveAverage
= eOperation
== ScConditionMode::AboveAverage
||
1037 eOperation
== ScConditionMode::AboveEqualAverage
;
1038 bool bEqualAverage
= eOperation
== ScConditionMode::AboveEqualAverage
||
1039 eOperation
== ScConditionMode::BelowEqualAverage
;
1040 bool bBottom
= eOperation
== ScConditionMode::Bottom10
1041 || eOperation
== ScConditionMode::BottomPercent
;
1042 bool bPercent
= eOperation
== ScConditionMode::TopPercent
||
1043 eOperation
== ScConditionMode::BottomPercent
;
1044 OUString
aRank(u
"0"_ustr
);
1045 if(IsTopBottomRule(eOperation
))
1047 // position and formula grammar are not important
1048 // we only store a number there
1049 aRank
= mrFormatEntry
.GetExpression(ScAddress(0,0,0), 0);
1052 if(IsTextRule(eOperation
))
1054 // we need to write the text without quotes
1055 // we have to actually get the string from
1056 // the token array for that
1057 std::unique_ptr
<ScTokenArray
> pTokenArray(mrFormatEntry
.CreateFlatCopiedTokenArray(0));
1058 if(pTokenArray
->GetLen())
1060 formula::StackVar eType
= pTokenArray
->FirstToken()->GetType();
1063 case formula::svDouble
:
1065 aText
= OString::number(pTokenArray
->FirstToken()->GetDouble());
1070 aText
= pTokenArray
->FirstToken()->GetString().getString().toUtf8();
1077 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1078 rWorksheet
->startElement( XML_cfRule
,
1079 XML_type
, GetTypeString( mrFormatEntry
.GetOperation() ),
1080 XML_priority
, OString::number(mnPriority
+ 1),
1081 XML_operator
, GetOperatorString( mrFormatEntry
.GetOperation(), bFmla2
),
1082 XML_aboveAverage
, ToPsz10(bAboveAverage
),
1083 XML_equalAverage
, ToPsz10(bEqualAverage
),
1084 XML_bottom
, ToPsz10(bBottom
),
1085 XML_percent
, ToPsz10(bPercent
),
1088 XML_dxfId
, OString::number(GetDxfs().GetDxfId(mrFormatEntry
.GetStyle())) );
1090 if (RequiresFixedFormula(eOperation
))
1092 rWorksheet
->startElement(XML_formula
);
1093 OString aFormula
= GetFixedFormula(eOperation
, maOrigin
, aText
);
1094 rWorksheet
->writeEscaped(aFormula
.getStr());
1095 rWorksheet
->endElement( XML_formula
);
1097 else if(RequiresFormula(eOperation
))
1099 rWorksheet
->startElement(XML_formula
);
1100 std::unique_ptr
<ScTokenArray
> pTokenArray(mrFormatEntry
.CreateFlatCopiedTokenArray(0));
1101 rWorksheet
->writeEscaped(XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormatEntry
.GetValidSrcPos(),
1102 pTokenArray
.get()));
1103 rWorksheet
->endElement( XML_formula
);
1106 rWorksheet
->startElement(XML_formula
);
1107 std::unique_ptr
<ScTokenArray
> pTokenArray2(mrFormatEntry
.CreateFlatCopiedTokenArray(1));
1108 rWorksheet
->writeEscaped(XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormatEntry
.GetValidSrcPos(),
1109 pTokenArray2
.get()));
1110 rWorksheet
->endElement( XML_formula
);
1113 // OOXTODO: XML_extLst
1114 rWorksheet
->endElement( XML_cfRule
);
1117 XclExpCF::XclExpCF( const XclExpRoot
& rRoot
, const ScCondFormatEntry
& rFormatEntry
, sal_Int32 nPriority
, ScAddress aOrigin
) :
1118 XclExpRecord( EXC_ID_CF
),
1119 XclExpRoot( rRoot
),
1120 mxImpl( new XclExpCFImpl( rRoot
, rFormatEntry
, nPriority
, aOrigin
) )
1124 XclExpCF::~XclExpCF()
1128 void XclExpCF::WriteBody( XclExpStream
& rStrm
)
1130 mxImpl
->WriteBody( rStrm
);
1133 void XclExpCF::SaveXml( XclExpXmlStream
& rStrm
)
1135 mxImpl
->SaveXml( rStrm
);
1138 XclExpDateFormat::XclExpDateFormat( const XclExpRoot
& rRoot
, const ScCondDateFormatEntry
& rFormatEntry
, sal_Int32 nPriority
):
1139 XclExpRecord( EXC_ID_CF
),
1140 XclExpRoot( rRoot
),
1141 mrFormatEntry(rFormatEntry
),
1142 mnPriority(nPriority
)
1146 XclExpDateFormat::~XclExpDateFormat()
1152 const char* getTimePeriodString( condformat::ScCondFormatDateType eType
)
1156 case condformat::TODAY
:
1158 case condformat::YESTERDAY
:
1160 case condformat::TOMORROW
:
1162 case condformat::THISWEEK
:
1164 case condformat::LASTWEEK
:
1166 case condformat::NEXTWEEK
:
1168 case condformat::THISMONTH
:
1170 case condformat::LASTMONTH
:
1172 case condformat::NEXTMONTH
:
1174 case condformat::LAST7DAYS
:
1184 void XclExpDateFormat::SaveXml( XclExpXmlStream
& rStrm
)
1186 // only write the supported entries into OOXML
1187 const char* sTimePeriod
= getTimePeriodString(mrFormatEntry
.GetDateType());
1191 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1192 rWorksheet
->startElement( XML_cfRule
,
1193 XML_type
, "timePeriod",
1194 XML_priority
, OString::number(mnPriority
+ 1),
1195 XML_timePeriod
, sTimePeriod
,
1196 XML_dxfId
, OString::number(GetDxfs().GetDxfId(mrFormatEntry
.GetStyleName())) );
1197 rWorksheet
->endElement( XML_cfRule
);
1200 XclExpCfvo::XclExpCfvo(const XclExpRoot
& rRoot
, const ScColorScaleEntry
& rEntry
, const ScAddress
& rAddr
, bool bFirst
):
1201 XclExpRoot( rRoot
),
1210 OString
getColorScaleType( const ScColorScaleEntry
& rEntry
, bool bFirst
)
1212 switch(rEntry
.GetType())
1214 case COLORSCALE_MIN
:
1216 case COLORSCALE_MAX
:
1218 case COLORSCALE_PERCENT
:
1219 return "percent"_ostr
;
1220 case COLORSCALE_FORMULA
:
1221 return "formula"_ostr
;
1222 case COLORSCALE_AUTO
:
1227 case COLORSCALE_PERCENTILE
:
1228 return "percentile"_ostr
;
1238 void XclExpCfvo::SaveXml( XclExpXmlStream
& rStrm
)
1240 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1243 if(mrEntry
.GetType() == COLORSCALE_FORMULA
)
1245 OUString aFormula
= XclXmlUtils::ToOUString( GetCompileFormulaContext(), maSrcPos
,
1246 mrEntry
.GetFormula());
1247 aValue
= OUStringToOString(aFormula
, RTL_TEXTENCODING_UTF8
);
1251 aValue
= OString::number( mrEntry
.GetValue() );
1254 rWorksheet
->startElement( XML_cfvo
,
1255 XML_type
, getColorScaleType(mrEntry
, mbFirst
),
1257 XML_gte
, sax_fastparser::UseIf("0", !mrEntry
.GetGreaterThanOrEqual()));
1259 rWorksheet
->endElement( XML_cfvo
);
1262 XclExpColScaleCol::XclExpColScaleCol( const XclExpRoot
& rRoot
, const Color
& rColor
):
1263 XclExpRoot( rRoot
),
1268 XclExpColScaleCol::~XclExpColScaleCol()
1272 void XclExpColScaleCol::SaveXml( XclExpXmlStream
& rStrm
)
1274 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1276 rWorksheet
->startElement(XML_color
, XML_rgb
, XclXmlUtils::ToOString(mrColor
));
1278 rWorksheet
->endElement( XML_color
);
1283 OString
createHexStringFromDigit(sal_uInt8 nDigit
)
1285 OString aString
= OString::number( nDigit
, 16 );
1286 if(aString
.getLength() == 1)
1287 aString
+= OString::number(0);
1291 OString
createGuidStringFromInt(sal_uInt8 nGuid
[16])
1293 OStringBuffer
aBuffer("{");
1294 for(size_t i
= 0; i
< 16; ++i
)
1296 aBuffer
.append(createHexStringFromDigit(nGuid
[i
]));
1297 if(i
== 3|| i
== 5 || i
== 7 || i
== 9 )
1298 aBuffer
.append('-');
1300 aBuffer
.append('}');
1301 OString aString
= aBuffer
.makeStringAndClear();
1302 return aString
.toAsciiUpperCase();
1305 OString
generateGUIDString()
1307 sal_uInt8 nGuid
[16];
1308 rtl_createUuid(nGuid
, nullptr, true);
1309 return createGuidStringFromInt(nGuid
);
1314 XclExpCondfmt::XclExpCondfmt( const XclExpRoot
& rRoot
, const ScConditionalFormat
& rCondFormat
, const XclExtLstRef
& xExtLst
, sal_Int32
& rIndex
) :
1315 XclExpRecord( EXC_ID_CONDFMT
),
1318 const ScRangeList
& aScRanges
= rCondFormat
.GetRange();
1319 GetAddressConverter().ConvertRangeList( maXclRanges
, aScRanges
, true );
1320 if( maXclRanges
.empty() )
1323 std::vector
<XclExpExtCondFormatData
> aExtEntries
;
1324 ScAddress aOrigin
= aScRanges
.Combine().aStart
;
1325 for( size_t nIndex
= 0, nCount
= rCondFormat
.size(); nIndex
< nCount
; ++nIndex
)
1326 if( const ScFormatEntry
* pFormatEntry
= rCondFormat
.GetEntry( nIndex
) )
1328 if(pFormatEntry
->GetType() == ScFormatEntry::Type::Condition
)
1329 maCFList
.AppendNewRecord( new XclExpCF( GetRoot(), static_cast<const ScCondFormatEntry
&>(*pFormatEntry
), ++rIndex
, aOrigin
) );
1330 else if(pFormatEntry
->GetType() == ScFormatEntry::Type::ExtCondition
)
1332 const ScCondFormatEntry
& rFormat
= static_cast<const ScCondFormatEntry
&>(*pFormatEntry
);
1333 XclExpExtCondFormatData aExtEntry
;
1334 aExtEntry
.nPriority
= ++rIndex
;
1335 aExtEntry
.aGUID
= generateGUIDString();
1336 aExtEntry
.pEntry
= &rFormat
;
1337 aExtEntries
.push_back(aExtEntry
);
1339 else if(pFormatEntry
->GetType() == ScFormatEntry::Type::Colorscale
)
1340 maCFList
.AppendNewRecord( new XclExpColorScale( GetRoot(), static_cast<const ScColorScaleFormat
&>(*pFormatEntry
), ++rIndex
) );
1341 else if(pFormatEntry
->GetType() == ScFormatEntry::Type::Databar
)
1343 const ScDataBarFormat
& rFormat
= static_cast<const ScDataBarFormat
&>(*pFormatEntry
);
1344 XclExpExtCondFormatData aExtEntry
;
1345 aExtEntry
.nPriority
= -1;
1346 aExtEntry
.aGUID
= generateGUIDString();
1347 aExtEntry
.pEntry
= &rFormat
;
1348 aExtEntries
.push_back(aExtEntry
);
1350 maCFList
.AppendNewRecord( new XclExpDataBar( GetRoot(), rFormat
, ++rIndex
, aExtEntry
.aGUID
));
1352 else if(pFormatEntry
->GetType() == ScFormatEntry::Type::Iconset
)
1354 // don't export iconSet entries that are not in OOXML
1355 const ScIconSetFormat
& rIconSet
= static_cast<const ScIconSetFormat
&>(*pFormatEntry
);
1356 bool bNeedsExt
= false;
1357 switch (rIconSet
.GetIconSetData()->eIconSetType
)
1359 case IconSet_3Smilies
:
1360 case IconSet_3ColorSmilies
:
1361 case IconSet_3Stars
:
1362 case IconSet_3Triangles
:
1363 case IconSet_5Boxes
:
1372 bNeedsExt
|= rIconSet
.GetIconSetData()->mbCustom
;
1376 XclExpExtCondFormatData aExtEntry
;
1377 aExtEntry
.nPriority
= ++rIndex
;
1378 aExtEntry
.aGUID
= generateGUIDString();
1379 aExtEntry
.pEntry
= &rIconSet
;
1380 aExtEntries
.push_back(aExtEntry
);
1383 maCFList
.AppendNewRecord( new XclExpIconSet( GetRoot(), rIconSet
, ++rIndex
) );
1385 else if(pFormatEntry
->GetType() == ScFormatEntry::Type::Date
)
1386 maCFList
.AppendNewRecord( new XclExpDateFormat( GetRoot(), static_cast<const ScCondDateFormatEntry
&>(*pFormatEntry
), ++rIndex
) );
1388 aScRanges
.Format( msSeqRef
, ScRefFlags::VALID
, GetDoc(), formula::FormulaGrammar::CONV_XL_OOX
, ' ', true );
1390 if(!aExtEntries
.empty() && xExtLst
)
1392 XclExpExt
* pParent
= xExtLst
->GetItem( XclExpExtDataBarType
);
1395 xExtLst
->AddRecord( new XclExpExtCondFormat( *xExtLst
) );
1396 pParent
= xExtLst
->GetItem( XclExpExtDataBarType
);
1398 static_cast<XclExpExtCondFormat
*>(xExtLst
->GetItem( XclExpExtDataBarType
))->AddRecord(
1399 new XclExpExtConditionalFormatting( *pParent
, aExtEntries
, aScRanges
));
1403 XclExpCondfmt::~XclExpCondfmt()
1407 bool XclExpCondfmt::IsValidForBinary() const
1409 // ccf (2 bytes): An unsigned integer that specifies the count of CF records that follow this
1410 // record. MUST be greater than or equal to 0x0001, and less than or equal to 0x0003.
1412 SAL_WARN_IF( maCFList
.GetSize() > 3, "sc.filter", "More than 3 conditional filters for cell(s), won't export");
1414 return !maCFList
.IsEmpty() && maCFList
.GetSize() <= 3 && !maXclRanges
.empty();
1417 bool XclExpCondfmt::IsValidForXml() const
1419 return !maCFList
.IsEmpty() && !maXclRanges
.empty();
1422 void XclExpCondfmt::Save( XclExpStream
& rStrm
)
1424 if( IsValidForBinary() )
1426 XclExpRecord::Save( rStrm
);
1427 maCFList
.Save( rStrm
);
1431 void XclExpCondfmt::WriteBody( XclExpStream
& rStrm
)
1433 OSL_ENSURE( !maCFList
.IsEmpty(), "XclExpCondfmt::WriteBody - no CF records to write" );
1434 OSL_ENSURE( !maXclRanges
.empty(), "XclExpCondfmt::WriteBody - no cell ranges found" );
1436 rStrm
<< static_cast< sal_uInt16
>( maCFList
.GetSize() )
1438 << maXclRanges
.GetEnclosingRange()
1442 void XclExpCondfmt::SaveXml( XclExpXmlStream
& rStrm
)
1444 if( !IsValidForXml() )
1447 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1448 rWorksheet
->startElement( XML_conditionalFormatting
,
1450 // OOXTODO: XML_pivot
1453 maCFList
.SaveXml( rStrm
);
1455 rWorksheet
->endElement( XML_conditionalFormatting
);
1458 XclExpColorScale::XclExpColorScale( const XclExpRoot
& rRoot
, const ScColorScaleFormat
& rFormat
, sal_Int32 nPriority
):
1459 XclExpRoot( rRoot
),
1460 mnPriority( nPriority
)
1462 const ScRange
& rRange
= rFormat
.GetRange().front();
1463 ScAddress aAddr
= rRange
.aStart
;
1464 for(const auto& rxColorScaleEntry
: rFormat
)
1466 // exact position is not important, we allow only absolute refs
1468 XclExpCfvoList::RecordRefType
xCfvo( new XclExpCfvo( GetRoot(), *rxColorScaleEntry
, aAddr
) );
1469 maCfvoList
.AppendRecord( xCfvo
);
1470 XclExpColScaleColList::RecordRefType
xClo( new XclExpColScaleCol( GetRoot(), rxColorScaleEntry
->GetColor() ) );
1471 maColList
.AppendRecord( xClo
);
1475 void XclExpColorScale::SaveXml( XclExpXmlStream
& rStrm
)
1477 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1479 rWorksheet
->startElement( XML_cfRule
,
1480 XML_type
, "colorScale",
1481 XML_priority
, OString::number(mnPriority
+ 1) );
1483 rWorksheet
->startElement(XML_colorScale
);
1485 maCfvoList
.SaveXml(rStrm
);
1486 maColList
.SaveXml(rStrm
);
1488 rWorksheet
->endElement( XML_colorScale
);
1490 rWorksheet
->endElement( XML_cfRule
);
1493 XclExpDataBar::XclExpDataBar( const XclExpRoot
& rRoot
, const ScDataBarFormat
& rFormat
, sal_Int32 nPriority
, OString aGUID
):
1494 XclExpRoot( rRoot
),
1495 mrFormat( rFormat
),
1496 mnPriority( nPriority
),
1497 maGUID(std::move(aGUID
))
1499 const ScRange
& rRange
= rFormat
.GetRange().front();
1500 ScAddress aAddr
= rRange
.aStart
;
1501 // exact position is not important, we allow only absolute refs
1502 mpCfvoLowerLimit
.reset(
1503 new XclExpCfvo(GetRoot(), *mrFormat
.GetDataBarData()->mpLowerLimit
, aAddr
, true));
1504 mpCfvoUpperLimit
.reset(
1505 new XclExpCfvo(GetRoot(), *mrFormat
.GetDataBarData()->mpUpperLimit
, aAddr
, false));
1507 mpCol
.reset( new XclExpColScaleCol( GetRoot(), mrFormat
.GetDataBarData()->maPositiveColor
) );
1510 void XclExpDataBar::SaveXml( XclExpXmlStream
& rStrm
)
1512 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1514 rWorksheet
->startElement( XML_cfRule
,
1515 XML_type
, "dataBar",
1516 XML_priority
, OString::number(mnPriority
+ 1) );
1518 rWorksheet
->startElement( XML_dataBar
,
1519 XML_showValue
, ToPsz10(!mrFormat
.GetDataBarData()->mbOnlyBar
),
1520 XML_minLength
, OString::number(sal_uInt32(mrFormat
.GetDataBarData()->mnMinLength
)),
1521 XML_maxLength
, OString::number(sal_uInt32(mrFormat
.GetDataBarData()->mnMaxLength
)) );
1523 mpCfvoLowerLimit
->SaveXml(rStrm
);
1524 mpCfvoUpperLimit
->SaveXml(rStrm
);
1525 mpCol
->SaveXml(rStrm
);
1527 rWorksheet
->endElement( XML_dataBar
);
1529 // extLst entries for Excel 2010 and 2013
1530 rWorksheet
->startElement(XML_extLst
);
1531 rWorksheet
->startElement(XML_ext
,
1532 FSNS(XML_xmlns
, XML_x14
), rStrm
.getNamespaceURL(OOX_NS(xls14Lst
)),
1533 XML_uri
, "{B025F937-C7B1-47D3-B67F-A62EFF666E3E}");
1535 rWorksheet
->startElementNS( XML_x14
, XML_id
);
1536 rWorksheet
->write(maGUID
);
1537 rWorksheet
->endElementNS( XML_x14
, XML_id
);
1539 rWorksheet
->endElement( XML_ext
);
1540 rWorksheet
->endElement( XML_extLst
);
1542 rWorksheet
->endElement( XML_cfRule
);
1545 XclExpIconSet::XclExpIconSet( const XclExpRoot
& rRoot
, const ScIconSetFormat
& rFormat
, sal_Int32 nPriority
):
1546 XclExpRoot( rRoot
),
1547 mrFormat( rFormat
),
1548 mnPriority( nPriority
)
1550 const ScRange
& rRange
= rFormat
.GetRange().front();
1551 ScAddress aAddr
= rRange
.aStart
;
1552 for (auto const& itr
: rFormat
)
1554 // exact position is not important, we allow only absolute refs
1556 XclExpCfvoList::RecordRefType
xCfvo( new XclExpCfvo( GetRoot(), *itr
, aAddr
) );
1557 maCfvoList
.AppendRecord( xCfvo
);
1561 void XclExpIconSet::SaveXml( XclExpXmlStream
& rStrm
)
1563 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1565 rWorksheet
->startElement( XML_cfRule
,
1566 XML_type
, "iconSet",
1567 XML_priority
, OString::number(mnPriority
+ 1) );
1569 OUString aIconSetName
= ScIconSetFormat::getIconSetName(mrFormat
.GetIconSetData()->eIconSetType
);
1570 rWorksheet
->startElement( XML_iconSet
,
1571 XML_iconSet
, aIconSetName
,
1572 XML_showValue
, sax_fastparser::UseIf("0", !mrFormat
.GetIconSetData()->mbShowValue
),
1573 XML_reverse
, sax_fastparser::UseIf("1", mrFormat
.GetIconSetData()->mbReverse
));
1575 maCfvoList
.SaveXml( rStrm
);
1577 rWorksheet
->endElement( XML_iconSet
);
1578 rWorksheet
->endElement( XML_cfRule
);
1581 XclExpCondFormatBuffer::XclExpCondFormatBuffer( const XclExpRoot
& rRoot
, const XclExtLstRef
& xExtLst
) :
1584 if( const ScConditionalFormatList
* pCondFmtList
= GetDoc().GetCondFormList(GetCurrScTab()) )
1586 sal_Int32 nIndex
= 0;
1587 for( const auto& rxCondFmt
: *pCondFmtList
)
1589 XclExpCondfmtList::RecordRefType
xCondfmtRec( new XclExpCondfmt( GetRoot(), *rxCondFmt
, xExtLst
, nIndex
));
1590 if( xCondfmtRec
->IsValidForXml() )
1591 maCondfmtList
.AppendRecord( xCondfmtRec
);
1596 void XclExpCondFormatBuffer::Save( XclExpStream
& rStrm
)
1598 maCondfmtList
.Save( rStrm
);
1601 void XclExpCondFormatBuffer::SaveXml( XclExpXmlStream
& rStrm
)
1603 maCondfmtList
.SaveXml( rStrm
);
1606 // Validation =================================================================
1610 /** Writes a formula for the DV record. */
1611 void lclWriteDvFormula( XclExpStream
& rStrm
, const XclTokenArray
* pXclTokArr
)
1613 sal_uInt16 nFmlaSize
= pXclTokArr
? pXclTokArr
->GetSize() : 0;
1614 rStrm
<< nFmlaSize
<< sal_uInt16( 0 );
1616 pXclTokArr
->WriteArray( rStrm
);
1619 /** Writes a formula for the DV record, based on a single string. */
1620 void lclWriteDvFormula( XclExpStream
& rStrm
, const XclExpString
& rString
)
1622 // fake a formula with a single tStr token
1623 rStrm
<< static_cast< sal_uInt16
>( rString
.GetSize() + 1 )
1629 const char* lcl_GetValidationType( sal_uInt32 nFlags
)
1631 switch( nFlags
& EXC_DV_MODE_MASK
)
1633 case EXC_DV_MODE_ANY
: return "none";
1634 case EXC_DV_MODE_WHOLE
: return "whole";
1635 case EXC_DV_MODE_DECIMAL
: return "decimal";
1636 case EXC_DV_MODE_LIST
: return "list";
1637 case EXC_DV_MODE_DATE
: return "date";
1638 case EXC_DV_MODE_TIME
: return "time";
1639 case EXC_DV_MODE_TEXTLEN
: return "textLength";
1640 case EXC_DV_MODE_CUSTOM
: return "custom";
1645 const char* lcl_GetOperatorType( sal_uInt32 nFlags
)
1647 switch( nFlags
& EXC_DV_COND_MASK
)
1649 case EXC_DV_COND_BETWEEN
: return "between";
1650 case EXC_DV_COND_NOTBETWEEN
: return "notBetween";
1651 case EXC_DV_COND_EQUAL
: return "equal";
1652 case EXC_DV_COND_NOTEQUAL
: return "notEqual";
1653 case EXC_DV_COND_GREATER
: return "greaterThan";
1654 case EXC_DV_COND_LESS
: return "lessThan";
1655 case EXC_DV_COND_EQGREATER
: return "greaterThanOrEqual";
1656 case EXC_DV_COND_EQLESS
: return "lessThanOrEqual";
1661 const char* lcl_GetErrorType( sal_uInt32 nFlags
)
1663 switch (nFlags
& EXC_DV_ERROR_MASK
)
1665 case EXC_DV_ERROR_STOP
: return "stop";
1666 case EXC_DV_ERROR_WARNING
: return "warning";
1667 case EXC_DV_ERROR_INFO
: return "information";
1672 void lcl_SetValidationText(const OUString
& rText
, XclExpString
& rValidationText
)
1674 if ( !rText
.isEmpty() )
1676 // maximum length allowed in Excel is 255 characters
1677 if ( rText
.getLength() > 255 )
1679 OUStringBuffer
aBuf( rText
);
1680 rValidationText
.Assign(
1681 comphelper::string::truncateToLength(aBuf
, 255).makeStringAndClear() );
1684 rValidationText
.Assign( rText
);
1687 rValidationText
.Assign( '\0' );
1692 XclExpDV::XclExpDV( const XclExpRoot
& rRoot
, sal_uInt32 nScHandle
) :
1693 XclExpRecord( EXC_ID_DV
),
1694 XclExpRoot( rRoot
),
1696 mnScHandle( nScHandle
)
1698 if( const ScValidationData
* pValData
= GetDoc().GetValidationEntry( mnScHandle
) )
1700 // prompt box - empty string represented by single NUL character
1701 OUString aTitle
, aText
;
1702 bool bShowPrompt
= pValData
->GetInput( aTitle
, aText
);
1703 lcl_SetValidationText(aTitle
, maPromptTitle
);
1704 lcl_SetValidationText(aText
, maPromptText
);
1706 // error box - empty string represented by single NUL character
1707 ScValidErrorStyle eScErrorStyle
;
1708 bool bShowError
= pValData
->GetErrMsg( aTitle
, aText
, eScErrorStyle
);
1709 lcl_SetValidationText(aTitle
, maErrorTitle
);
1710 lcl_SetValidationText(aText
, maErrorText
);
1713 switch( pValData
->GetDataMode() )
1715 case SC_VALID_ANY
: mnFlags
|= EXC_DV_MODE_ANY
; break;
1716 case SC_VALID_WHOLE
: mnFlags
|= EXC_DV_MODE_WHOLE
; break;
1717 case SC_VALID_DECIMAL
: mnFlags
|= EXC_DV_MODE_DECIMAL
; break;
1718 case SC_VALID_LIST
: mnFlags
|= EXC_DV_MODE_LIST
; break;
1719 case SC_VALID_DATE
: mnFlags
|= EXC_DV_MODE_DATE
; break;
1720 case SC_VALID_TIME
: mnFlags
|= EXC_DV_MODE_TIME
; break;
1721 case SC_VALID_TEXTLEN
: mnFlags
|= EXC_DV_MODE_TEXTLEN
; break;
1722 case SC_VALID_CUSTOM
: mnFlags
|= EXC_DV_MODE_CUSTOM
; break;
1723 default: OSL_FAIL( "XclExpDV::XclExpDV - unknown mode" );
1726 switch( pValData
->GetOperation() )
1728 case ScConditionMode::NONE
:
1729 case ScConditionMode::Equal
: mnFlags
|= EXC_DV_COND_EQUAL
; break;
1730 case ScConditionMode::Less
: mnFlags
|= EXC_DV_COND_LESS
; break;
1731 case ScConditionMode::Greater
: mnFlags
|= EXC_DV_COND_GREATER
; break;
1732 case ScConditionMode::EqLess
: mnFlags
|= EXC_DV_COND_EQLESS
; break;
1733 case ScConditionMode::EqGreater
: mnFlags
|= EXC_DV_COND_EQGREATER
; break;
1734 case ScConditionMode::NotEqual
: mnFlags
|= EXC_DV_COND_NOTEQUAL
; break;
1735 case ScConditionMode::Between
: mnFlags
|= EXC_DV_COND_BETWEEN
; break;
1736 case ScConditionMode::NotBetween
: mnFlags
|= EXC_DV_COND_NOTBETWEEN
; break;
1737 default: OSL_FAIL( "XclExpDV::XclExpDV - unknown condition" );
1739 switch( eScErrorStyle
)
1741 case SC_VALERR_STOP
: mnFlags
|= EXC_DV_ERROR_STOP
; break;
1742 case SC_VALERR_WARNING
: mnFlags
|= EXC_DV_ERROR_WARNING
; break;
1743 case SC_VALERR_INFO
: mnFlags
|= EXC_DV_ERROR_INFO
; break;
1744 case SC_VALERR_MACRO
:
1745 // set INFO for validity with macro call, delete title
1746 mnFlags
|= EXC_DV_ERROR_INFO
;
1747 maErrorTitle
.Assign( '\0' ); // contains macro name
1749 default: OSL_FAIL( "XclExpDV::XclExpDV - unknown error style" );
1751 ::set_flag( mnFlags
, EXC_DV_IGNOREBLANK
, pValData
->IsIgnoreBlank() );
1752 ::set_flag( mnFlags
, EXC_DV_SUPPRESSDROPDOWN
, pValData
->GetListType() == css::sheet::TableValidationVisibility::INVISIBLE
);
1753 ::set_flag( mnFlags
, EXC_DV_SHOWPROMPT
, bShowPrompt
);
1754 ::set_flag( mnFlags
, EXC_DV_SHOWERROR
, bShowError
);
1757 XclExpFormulaCompiler
& rFmlaComp
= GetFormulaCompiler();
1760 std::unique_ptr
< ScTokenArray
> xScTokArr
= pValData
->CreateFlatCopiedTokenArray( 0 );
1763 if( pValData
->GetDataMode() == SC_VALID_LIST
)
1766 if( XclTokenArrayHelper::GetStringList( aString
, *xScTokArr
, '\n' ) )
1769 OUStringBuffer sListBuf
;
1770 OUStringBuffer
sFormulaBuf("\"");
1771 /* Formula is a list of string tokens -> build the Excel string.
1772 Data validity is BIFF8 only (important for the XclExpString object).
1773 Excel uses the NUL character as string list separator. */
1774 mxString1
.reset( new XclExpString( XclStrFlags::EightBitLength
) );
1775 if (!aString
.isEmpty())
1777 sal_Int32 nStringIx
= 0;
1780 const std::u16string_view
aToken( o3tl::getToken(aString
, 0, '\n', nStringIx
) );
1781 if (aToken
.find(',') != std::u16string_view::npos
)
1783 sListBuf
.append(OUString::Concat("\"") + aToken
+ "\"");
1787 sListBuf
.append(aToken
);
1788 mxString1
->Append( aToken
);
1789 sFormulaBuf
.append( aToken
);
1792 sal_Unicode cUnicodeChar
= 0;
1793 mxString1
->Append( std::u16string_view(&cUnicodeChar
, 1) );
1794 sFormulaBuf
.append( ',' );
1795 sListBuf
.append( ',' );
1798 ::set_flag( mnFlags
, EXC_DV_STRINGLIST
);
1800 // maximum length allowed in Excel is 255 characters, and don't end with an empty token
1801 // It should be 8192 but Excel doesn't accept it for unknown reason
1802 // See also https://bugs.documentfoundation.org/show_bug.cgi?id=137167#c2 for more details
1803 sal_uInt32 nLen
= sFormulaBuf
.getLength();
1804 if( nLen
> 256 ) // 255 + beginning quote mark
1807 if( sFormulaBuf
[nLen
- 1] == ',' )
1809 sFormulaBuf
.truncate(nLen
);
1812 sFormulaBuf
.append( '"' );
1813 msFormula1
= sFormulaBuf
.makeStringAndClear();
1815 msList
= sListBuf
.makeStringAndClear();
1817 sListBuf
.remove(0, sListBuf
.getLength());
1821 /* All other formulas in validation are stored like conditional
1822 formatting formulas (with tRefN/tAreaN tokens as value or
1823 array class). But NOT the cell references and defined names
1824 in list validation - they are stored as reference class
1826 1) Cell must be equal to A1 -> formula is =A1 -> writes tRefNV token
1827 2) List is taken from A1 -> formula is =A1 -> writes tRefNR token
1828 Formula compiler supports this by offering two different functions
1829 CreateDataValFormula() and CreateListValFormula(). */
1830 if(GetOutput() == EXC_OUTPUT_BINARY
)
1831 mxTokArr1
= rFmlaComp
.CreateFormula( EXC_FMLATYPE_LISTVAL
, *xScTokArr
);
1833 msFormula1
= XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData
->GetSrcPos(),
1839 // no list validation -> convert the formula
1840 if(GetOutput() == EXC_OUTPUT_BINARY
)
1841 mxTokArr1
= rFmlaComp
.CreateFormula( EXC_FMLATYPE_DATAVAL
, *xScTokArr
);
1843 msFormula1
= XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData
->GetSrcPos(),
1849 xScTokArr
= pValData
->CreateFlatCopiedTokenArray( 1 );
1852 if(GetOutput() == EXC_OUTPUT_BINARY
)
1853 mxTokArr2
= rFmlaComp
.CreateFormula( EXC_FMLATYPE_DATAVAL
, *xScTokArr
);
1855 msFormula2
= XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData
->GetSrcPos(),
1861 OSL_FAIL( "XclExpDV::XclExpDV - missing core data" );
1862 mnScHandle
= SAL_MAX_UINT32
;
1866 XclExpDV::~XclExpDV()
1870 void XclExpDV::InsertCellRange( const ScRange
& rRange
)
1872 maScRanges
.Join( rRange
);
1875 bool XclExpDV::Finalize()
1877 GetAddressConverter().ConvertRangeList( maXclRanges
, maScRanges
, true );
1878 return (mnScHandle
!= SAL_MAX_UINT32
) && !maXclRanges
.empty();
1881 void XclExpDV::WriteBody( XclExpStream
& rStrm
)
1883 // flags and strings
1884 rStrm
<< mnFlags
<< maPromptTitle
<< maErrorTitle
<< maPromptText
<< maErrorText
;
1885 // condition formulas
1887 lclWriteDvFormula( rStrm
, *mxString1
);
1889 lclWriteDvFormula( rStrm
, mxTokArr1
.get() );
1890 lclWriteDvFormula( rStrm
, mxTokArr2
.get() );
1892 rStrm
<< maXclRanges
;
1895 void XclExpDV::SaveXml( XclExpXmlStream
& rStrm
)
1897 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1898 rWorksheet
->startElement( XML_dataValidation
,
1899 XML_allowBlank
, ToPsz( ::get_flag( mnFlags
, EXC_DV_IGNOREBLANK
) ),
1900 XML_error
, XESTRING_TO_PSZ( maErrorText
),
1901 XML_errorStyle
, lcl_GetErrorType(mnFlags
),
1902 XML_errorTitle
, XESTRING_TO_PSZ( maErrorTitle
),
1903 // OOXTODO: XML_imeMode,
1904 XML_operator
, lcl_GetOperatorType( mnFlags
),
1905 XML_prompt
, XESTRING_TO_PSZ( maPromptText
),
1906 XML_promptTitle
, XESTRING_TO_PSZ( maPromptTitle
),
1907 // showDropDown should have been showNoDropDown - check oox/xlsx import for details
1908 XML_showDropDown
, ToPsz( ::get_flag( mnFlags
, EXC_DV_SUPPRESSDROPDOWN
) ),
1909 XML_showErrorMessage
, ToPsz( ::get_flag( mnFlags
, EXC_DV_SHOWERROR
) ),
1910 XML_showInputMessage
, ToPsz( ::get_flag( mnFlags
, EXC_DV_SHOWPROMPT
) ),
1911 XML_sqref
, XclXmlUtils::ToOString(rStrm
.GetRoot().GetDoc(), maScRanges
),
1912 XML_type
, lcl_GetValidationType(mnFlags
) );
1913 if (!msList
.isEmpty())
1915 rWorksheet
->startElement(FSNS(XML_mc
, XML_AlternateContent
),
1916 FSNS(XML_xmlns
, XML_x12ac
),rStrm
.getNamespaceURL(OOX_NS(x12ac
)),
1917 FSNS(XML_xmlns
, XML_mc
),rStrm
.getNamespaceURL(OOX_NS(mce
)));
1918 rWorksheet
->startElement(FSNS(XML_mc
, XML_Choice
), XML_Requires
, "x12ac");
1919 rWorksheet
->startElement(FSNS(XML_x12ac
, XML_list
));
1920 rWorksheet
->writeEscaped(msList
);
1921 rWorksheet
->endElement(FSNS(XML_x12ac
, XML_list
));
1922 rWorksheet
->endElement(FSNS(XML_mc
, XML_Choice
));
1923 rWorksheet
->startElement(FSNS(XML_mc
, XML_Fallback
));
1924 rWorksheet
->startElement(XML_formula1
);
1925 rWorksheet
->writeEscaped(msFormula1
);
1926 rWorksheet
->endElement(XML_formula1
);
1927 rWorksheet
->endElement(FSNS(XML_mc
, XML_Fallback
));
1928 rWorksheet
->endElement(FSNS(XML_mc
, XML_AlternateContent
));
1930 if (msList
.isEmpty() && !msFormula1
.isEmpty())
1932 rWorksheet
->startElement(XML_formula1
);
1933 rWorksheet
->writeEscaped( msFormula1
);
1934 rWorksheet
->endElement( XML_formula1
);
1936 if( !msFormula2
.isEmpty() )
1938 rWorksheet
->startElement(XML_formula2
);
1939 rWorksheet
->writeEscaped( msFormula2
);
1940 rWorksheet
->endElement( XML_formula2
);
1942 rWorksheet
->endElement( XML_dataValidation
);
1945 XclExpDval::XclExpDval( const XclExpRoot
& rRoot
) :
1946 XclExpRecord( EXC_ID_DVAL
, 18 ),
1951 XclExpDval::~XclExpDval()
1955 void XclExpDval::InsertCellRange( const ScRange
& rRange
, sal_uInt32 nScHandle
)
1957 if( GetBiff() == EXC_BIFF8
)
1959 XclExpDV
& rDVRec
= SearchOrCreateDv( nScHandle
);
1960 rDVRec
.InsertCellRange( rRange
);
1964 void XclExpDval::Save( XclExpStream
& rStrm
)
1966 // check all records
1967 size_t nPos
= maDVList
.GetSize();
1970 --nPos
; // backwards to keep nPos valid
1971 XclExpDVRef xDVRec
= maDVList
.GetRecord( nPos
);
1972 if( !xDVRec
->Finalize() )
1973 maDVList
.RemoveRecord( nPos
);
1976 // write the DVAL and the DV's
1977 if( !maDVList
.IsEmpty() )
1979 XclExpRecord::Save( rStrm
);
1980 maDVList
.Save( rStrm
);
1984 void XclExpDval::SaveXml( XclExpXmlStream
& rStrm
)
1986 if( maDVList
.IsEmpty() )
1989 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1990 rWorksheet
->startElement( XML_dataValidations
,
1991 XML_count
, OString::number(maDVList
.GetSize())
1992 // OOXTODO: XML_disablePrompts,
1993 // OOXTODO: XML_xWindow,
1994 // OOXTODO: XML_yWindow
1996 maDVList
.SaveXml( rStrm
);
1997 rWorksheet
->endElement( XML_dataValidations
);
2000 XclExpDV
& XclExpDval::SearchOrCreateDv( sal_uInt32 nScHandle
)
2002 // test last found record
2003 if( mxLastFoundDV
&& (mxLastFoundDV
->GetScHandle() == nScHandle
) )
2004 return *mxLastFoundDV
;
2007 size_t nCurrPos
= 0;
2008 if( !maDVList
.IsEmpty() )
2010 size_t nFirstPos
= 0;
2011 size_t nLastPos
= maDVList
.GetSize() - 1;
2013 sal_uInt32 nCurrScHandle
= ::std::numeric_limits
< sal_uInt32
>::max();
2014 while( (nFirstPos
<= nLastPos
) && bLoop
)
2016 nCurrPos
= (nFirstPos
+ nLastPos
) / 2;
2017 mxLastFoundDV
= maDVList
.GetRecord( nCurrPos
);
2018 nCurrScHandle
= mxLastFoundDV
->GetScHandle();
2019 if( nCurrScHandle
== nScHandle
)
2021 else if( nCurrScHandle
< nScHandle
)
2022 nFirstPos
= nCurrPos
+ 1;
2024 nLastPos
= nCurrPos
- 1;
2025 else // special case for nLastPos = -1
2028 if( nCurrScHandle
== nScHandle
)
2029 return *mxLastFoundDV
;
2030 else if( nCurrScHandle
< nScHandle
)
2034 // create new DV record
2035 mxLastFoundDV
= new XclExpDV( *this, nScHandle
);
2036 maDVList
.InsertRecord( mxLastFoundDV
, nCurrPos
);
2037 return *mxLastFoundDV
;
2040 void XclExpDval::WriteBody( XclExpStream
& rStrm
)
2042 rStrm
.WriteZeroBytes( 10 );
2043 rStrm
<< EXC_DVAL_NOOBJ
<< static_cast< sal_uInt32
>( maDVList
.GetSize() );
2046 // Web Queries ================================================================
2048 XclExpWebQuery::XclExpWebQuery(
2049 const OUString
& rRangeName
,
2050 const OUString
& rUrl
,
2051 std::u16string_view rSource
,
2052 sal_Int32 nRefrSecs
) :
2053 maDestRange( rRangeName
),
2055 // refresh delay time: seconds -> minutes
2056 mnRefresh( ulimit_cast
< sal_Int16
>( (nRefrSecs
+ 59) / 60 ) ),
2057 mbEntireDoc( false )
2059 // comma separated list of HTML table names or indexes
2060 OUString aNewTables
;
2061 OUString aAppendTable
;
2062 bool bExitLoop
= false;
2063 if (!rSource
.empty())
2065 sal_Int32 nStringIx
= 0;
2068 OUString
aToken( o3tl::getToken(rSource
, 0, ';', nStringIx
) );
2069 mbEntireDoc
= ScfTools::IsHTMLDocName( aToken
);
2070 bExitLoop
= mbEntireDoc
|| ScfTools::IsHTMLTablesName( aToken
);
2071 if( !bExitLoop
&& ScfTools::GetHTMLNameFromName( aToken
, aAppendTable
) )
2072 aNewTables
= ScGlobal::addToken( aNewTables
, aAppendTable
, ',' );
2074 while (nStringIx
>0 && !bExitLoop
);
2077 if( !bExitLoop
) // neither HTML_all nor HTML_tables found
2079 if( !aNewTables
.isEmpty() )
2080 mxQryTables
.reset( new XclExpString( aNewTables
) );
2086 XclExpWebQuery::~XclExpWebQuery()
2090 void XclExpWebQuery::Save( XclExpStream
& rStrm
)
2092 OSL_ENSURE( !mbEntireDoc
|| !mxQryTables
, "XclExpWebQuery::Save - illegal mode" );
2096 rStrm
.StartRecord( EXC_ID_QSI
, 10 + maDestRange
.GetSize() );
2097 rStrm
<< EXC_QSI_DEFAULTFLAGS
2098 << sal_uInt16( 0x0010 )
2099 << sal_uInt16( 0x0012 )
2100 << sal_uInt32( 0x00000000 )
2106 ::insert_value( nFlags
, EXC_PQRYTYPE_WEBQUERY
, 0, 3 );
2107 ::set_flag( nFlags
, EXC_PQRY_WEBQUERY
);
2108 ::set_flag( nFlags
, EXC_PQRY_TABLES
, !mbEntireDoc
);
2109 rStrm
.StartRecord( EXC_ID_PQRY
, 12 );
2111 << sal_uInt16( 0x0000 )
2112 << sal_uInt16( 0x0001 );
2113 rStrm
.WriteZeroBytes( 6 );
2117 rStrm
.StartRecord( EXC_ID_WQSTRING
, maUrl
.GetSize() );
2121 // unknown record 0x0802
2122 rStrm
.StartRecord( EXC_ID_0802
, 16 + maDestRange
.GetSize() );
2123 rStrm
<< EXC_ID_0802
; // repeated record id ?!?
2124 rStrm
.WriteZeroBytes( 6 );
2125 rStrm
<< sal_uInt16( 0x0003 )
2126 << sal_uInt32( 0x00000000 )
2127 << sal_uInt16( 0x0010 )
2131 // WEBQRYSETTINGS record
2132 nFlags
= mxQryTables
? EXC_WQSETT_SPECTABLES
: EXC_WQSETT_ALL
;
2133 rStrm
.StartRecord( EXC_ID_WQSETT
, 28 );
2134 rStrm
<< EXC_ID_WQSETT
// repeated record id ?!?
2135 << sal_uInt16( 0x0000 )
2136 << sal_uInt16( 0x0004 )
2137 << sal_uInt16( 0x0000 )
2138 << EXC_WQSETT_DEFAULTFLAGS
2140 rStrm
.WriteZeroBytes( 10 );
2141 rStrm
<< mnRefresh
// refresh delay in minutes
2142 << EXC_WQSETT_FORMATFULL
2143 << sal_uInt16( 0x0000 );
2146 // WEBQRYTABLES record
2149 rStrm
.StartRecord( EXC_ID_WQTABLES
, 4 + mxQryTables
->GetSize() );
2150 rStrm
<< EXC_ID_WQTABLES
// repeated record id ?!?
2151 << sal_uInt16( 0x0000 )
2152 << *mxQryTables
; // comma separated list of source tables
2157 XclExpWebQueryBuffer::XclExpWebQueryBuffer( const XclExpRoot
& rRoot
)
2159 SCTAB nScTab
= rRoot
.GetCurrScTab();
2160 ScDocShell
* pShell
= rRoot
.GetDocShell();
2161 if( !pShell
) return;
2162 ScfPropertySet
aModelProp( pShell
->GetModel() );
2163 if( !aModelProp
.Is() ) return;
2165 Reference
< XAreaLinks
> xAreaLinks
;
2166 aModelProp
.GetProperty( xAreaLinks
, SC_UNO_AREALINKS
);
2167 if( !xAreaLinks
.is() ) return;
2169 for( sal_Int32 nIndex
= 0, nCount
= xAreaLinks
->getCount(); nIndex
< nCount
; ++nIndex
)
2171 Reference
< XAreaLink
> xAreaLink( xAreaLinks
->getByIndex( nIndex
), UNO_QUERY
);
2172 if( xAreaLink
.is() )
2174 CellRangeAddress
aDestRange( xAreaLink
->getDestArea() );
2175 if( static_cast< SCTAB
>( aDestRange
.Sheet
) == nScTab
)
2177 ScfPropertySet
aLinkProp( xAreaLink
);
2179 if( aLinkProp
.GetProperty( aFilter
, SC_UNONAME_FILTER
) &&
2180 (aFilter
== EXC_WEBQRY_FILTER
) )
2183 OUString
/*aFilterOpt,*/ aUrl
;
2184 sal_Int32 nRefresh
= 0;
2186 // aLinkProp.GetProperty( aFilterOpt, SC_UNONAME_FILTOPT );
2187 aLinkProp
.GetProperty( aUrl
, SC_UNONAME_LINKURL
);
2188 aLinkProp
.GetProperty( nRefresh
, SC_UNONAME_REFDELAY
);
2190 OUString
aAbsDoc( ScGlobal::GetAbsDocName( aUrl
, pShell
) );
2191 INetURLObject
aUrlObj( aAbsDoc
);
2192 OUString
aWebQueryUrl( aUrlObj
.getFSysPath( FSysStyle::Dos
) );
2193 if( aWebQueryUrl
.isEmpty() )
2194 aWebQueryUrl
= aAbsDoc
;
2196 // find range or create a new range
2197 OUString aRangeName
;
2198 ScRange aScDestRange
;
2199 ScUnoConversion::FillScRange( aScDestRange
, aDestRange
);
2200 if( const ScRangeData
* pRangeData
= rRoot
.GetNamedRanges().findByRange( aScDestRange
) )
2202 aRangeName
= pRangeData
->GetName();
2206 XclExpFormulaCompiler
& rFmlaComp
= rRoot
.GetFormulaCompiler();
2207 XclExpNameManager
& rNameMgr
= rRoot
.GetNameManager();
2209 // create a new unique defined name containing the range
2210 XclTokenArrayRef xTokArr
= rFmlaComp
.CreateFormula( EXC_FMLATYPE_WQUERY
, aScDestRange
);
2211 sal_uInt16 nNameIdx
= rNameMgr
.InsertUniqueName( aUrlObj
.getBase(), xTokArr
, nScTab
);
2212 aRangeName
= rNameMgr
.GetOrigName( nNameIdx
);
2215 // create and store the web query record
2216 if( !aRangeName
.isEmpty() )
2217 AppendNewRecord( new XclExpWebQuery(
2218 aRangeName
, aWebQueryUrl
, xAreaLink
->getSourceArea(), nRefresh
) );
2225 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */