1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <svx/sdtaitm.hxx>
21 #include <svx/svdotext.hxx>
22 #include <editeng/editobj.hxx>
23 #include <svx/svdoole2.hxx>
24 #include <sot/storage.hxx>
25 #include <svl/itemset.hxx>
26 #include <svx/svdocapt.hxx>
27 #include <svx/unoapi.hxx>
28 #include <editeng/writingmodeitem.hxx>
29 #include <tools/urlobj.hxx>
30 
31 #include <rtl/math.hxx>
32 #include <rtl/uuid.h>
33 #include <sal/log.hxx>
34 #include <drwlayer.hxx>
35 
36 #include <root.hxx>
37 #include <xcl97rec.hxx>
38 #include <xcl97esc.hxx>
39 #include <xeescher.hxx>
40 #include <xehelper.hxx>
41 #include <xelink.hxx>
42 #include <xlcontent.hxx>
43 
44 #include <unotools/fltrcfg.hxx>
45 #include <editeng/adjustitem.hxx>
46 #include <editeng/eeitem.hxx>
47 #include <filter/msfilter/msoleexp.hxx>
48 
49 #include <unotools/localedatawrapper.hxx>
50 
51 #include <stdio.h>
52 
53 #include <document.hxx>
54 #include <rangelst.hxx>
55 #include <docoptio.hxx>
56 #include <tabprotection.hxx>
57 
58 #include <com/sun/star/embed/Aspects.hpp>
59 #include <com/sun/star/chart/XChartDocument.hpp>
60 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
61 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
62 #include <com/sun/star/chart2/XChartDocument.hpp>
63 
64 #include <sax/fastattribs.hxx>
65 #include <oox/token/tokens.hxx>
66 #include <oox/token/namespaces.hxx>
67 #include <oox/token/relationship.hxx>
68 #include <oox/export/shapes.hxx>
69 #include <oox/export/utils.hxx>
70 #include <oox/export/vmlexport.hxx>
71 #include <detfunc.hxx>
72 
73 #include <memory>
74 
75 using namespace ::com::sun::star;
76 using ::com::sun::star::uno::Reference;
77 using ::com::sun::star::uno::UNO_QUERY;
78 using ::com::sun::star::beans::XPropertySet;
79 using ::com::sun::star::drawing::XShape;
80 using ::oox::drawingml::ShapeExport;
81 using ::oox::vml::VMLExport;
82 using namespace oox;
83 
XclExpObjList(const XclExpRoot & rRoot,XclEscherEx & rEscherEx)84 XclExpObjList::XclExpObjList( const XclExpRoot& rRoot, XclEscherEx& rEscherEx ) :
85     XclExpRoot( rRoot ),
86     mnScTab( rRoot.GetCurrScTab() ),
87     mrEscherEx( rEscherEx )
88 {
89     pMsodrawingPerSheet.reset( new XclExpMsoDrawing( rEscherEx ) );
90     // open the DGCONTAINER and the patriarch group shape
91     mrEscherEx.OpenContainer( ESCHER_DgContainer );
92     tools::Rectangle aRect( 0, 0, 0, 0 );
93     mrEscherEx.EnterGroup( &aRect );
94     mrEscherEx.UpdateDffFragmentEnd();
95 }
96 
~XclExpObjList()97 XclExpObjList::~XclExpObjList()
98 {
99     maObjs.clear();
100     pMsodrawingPerSheet.reset();
101     pSolverContainer.reset();
102 }
103 
Add(std::unique_ptr<XclObj> pObj)104 sal_uInt16 XclExpObjList::Add( std::unique_ptr<XclObj> pObj )
105 {
106     OSL_ENSURE( maObjs.size() < 0xFFFF, "XclExpObjList::Add: too much for Xcl" );
107 
108     size_t nSize = maObjs.size();
109 
110     if ( nSize < 0xFFFF )
111     {
112         pObj->SetId( nSize+1 );
113         pObj->SetTab( mnScTab );
114         maObjs.push_back(std::move(pObj));
115         ++nSize;
116     }
117     else
118     {
119         nSize = 0;
120     }
121 
122     return nSize;
123 }
124 
pop_back()125 std::unique_ptr<XclObj> XclExpObjList::pop_back ()
126 {
127     auto ret = std::move(maObjs.back());
128     maObjs.pop_back();
129     return ret;
130 }
131 
EndSheet()132 void XclExpObjList::EndSheet()
133 {
134     // Is there still something in the stream? -> The solver container
135     if( mrEscherEx.HasPendingDffData() )
136         pSolverContainer.reset( new XclExpMsoDrawing( mrEscherEx ) );
137 
138     // close the DGCONTAINER created by XclExpObjList ctor MSODRAWING
139     mrEscherEx.CloseContainer();
140 }
141 
Save(XclExpStream & rStrm)142 void XclExpObjList::Save( XclExpStream& rStrm )
143 {
144     //! Escher must be written, even if there are no objects
145     pMsodrawingPerSheet->Save( rStrm );
146 
147     for ( const auto& rxObj : maObjs )
148         rxObj->Save( rStrm );
149 
150     if( pSolverContainer )
151         pSolverContainer->Save( rStrm );
152 }
153 
154 namespace {
155 
IsFormControlObject(const XclObj * rObj)156 bool IsFormControlObject( const XclObj *rObj )
157 {
158     switch( rObj->GetObjType() )
159     {
160         case EXC_OBJTYPE_CHECKBOX:
161             return true;
162         default:
163             return false;
164     }
165 }
166 
IsVmlObject(const XclObj * rObj)167 bool IsVmlObject( const XclObj *rObj )
168 {
169     switch( rObj->GetObjType() )
170     {
171         case EXC_OBJTYPE_NOTE:
172             return true;
173         default:
174             return false;
175     }
176 }
177 
GetVmlObjectCount(XclExpObjList & rList)178 sal_Int32 GetVmlObjectCount( XclExpObjList& rList )
179 {
180     return static_cast<sal_Int32>(std::count_if(rList.begin(), rList.end(),
181         [](const std::unique_ptr<XclObj>& rxObj) { return IsVmlObject( rxObj.get() ); }));
182 }
183 
IsValidObject(const XclObj & rObj)184 bool IsValidObject( const XclObj& rObj )
185 {
186     if (rObj.GetObjType() == EXC_OBJTYPE_CHART)
187     {
188         // Chart object.  Make sure it's a valid chart object.  We skip
189         // invalid chart objects from exporting to prevent Excel from
190         // complaining on load.
191 
192         const XclExpChartObj& rChartObj = static_cast<const XclExpChartObj&>(rObj);
193         uno::Reference<chart2::XChartDocument> xChartDoc(rChartObj.GetChartDoc(), uno::UNO_QUERY);
194         if (!xChartDoc.is())
195             return false;
196 
197         uno::Reference<chart2::XDiagram> xDiagram = xChartDoc->getFirstDiagram();
198         if (!xDiagram.is())
199             return false;
200 
201         uno::Reference<chart2::XCoordinateSystemContainer> xCooSysContainer(xDiagram, uno::UNO_QUERY);
202         if (!xCooSysContainer.is())
203             return false;
204 
205         const uno::Sequence<uno::Reference<chart2::XCoordinateSystem>> xCooSysSeq = xCooSysContainer->getCoordinateSystems();
206         for (const auto& rCooSys : xCooSysSeq)
207         {
208             Reference<chart2::XChartTypeContainer> xChartTypeCont(rCooSys, uno::UNO_QUERY);
209             if (!xChartTypeCont.is())
210                 return false;
211 
212             uno::Sequence<uno::Reference<chart2::XChartType>> xChartTypeSeq = xChartTypeCont->getChartTypes();
213             if (!xChartTypeSeq.hasElements())
214                 // No chart type.  Not good.
215                 return false;
216         }
217     }
218 
219     return true;
220 }
221 
SaveDrawingMLObjects(XclExpObjList & rList,XclExpXmlStream & rStrm)222 void SaveDrawingMLObjects( XclExpObjList& rList, XclExpXmlStream& rStrm )
223 {
224     std::vector<XclObj*> aList;
225     aList.reserve(rList.size());
226     for (const auto& rxObj : rList)
227     {
228         if (IsVmlObject(rxObj.get()) || !IsValidObject(*rxObj))
229             continue;
230 
231         aList.push_back(rxObj.get());
232     }
233 
234     if (aList.empty())
235         return;
236 
237     sal_Int32 nDrawing = drawingml::DrawingML::getNewDrawingUniqueId();
238     OUString sId;
239     sax_fastparser::FSHelperPtr pDrawing = rStrm.CreateOutputStream(
240             XclXmlUtils::GetStreamName( "xl/", "drawings/drawing", nDrawing ),
241             XclXmlUtils::GetStreamName( "../", "drawings/drawing", nDrawing ),
242             rStrm.GetCurrentStream()->getOutputStream(),
243             "application/vnd.openxmlformats-officedocument.drawing+xml",
244             oox::getRelationship(Relationship::DRAWING),
245             &sId );
246 
247     rStrm.GetCurrentStream()->singleElement(XML_drawing, FSNS(XML_r, XML_id), sId.toUtf8());
248 
249     rStrm.PushStream( pDrawing );
250     pDrawing->startElement( FSNS( XML_xdr, XML_wsDr ),
251             FSNS(XML_xmlns, XML_xdr), rStrm.getNamespaceURL(OOX_NS(dmlSpreadDr)).toUtf8(),
252             FSNS(XML_xmlns, XML_a),   rStrm.getNamespaceURL(OOX_NS(dml)).toUtf8(),
253             FSNS(XML_xmlns, XML_r),   rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8() );
254 
255     sal_Int32 nShapeId = 1000; // unique id of the shape inside one worksheet (not the whole document)
256     for (const auto& rpObj : aList)
257     {
258         // validate shapeId
259         if ( IsFormControlObject( rpObj ) )
260         {
261             XclExpTbxControlObj* pXclExpTbxControlObj = dynamic_cast<XclExpTbxControlObj*>(rpObj);
262             if (pXclExpTbxControlObj)
263             {
264                 pXclExpTbxControlObj->setShapeId(++nShapeId);
265             }
266         }
267 
268         rpObj->SaveXml(rStrm);
269     }
270 
271     pDrawing->endElement( FSNS( XML_xdr, XML_wsDr ) );
272 
273     rStrm.PopStream();
274 }
275 
SaveFormControlObjects(XclExpObjList & rList,XclExpXmlStream & rStrm)276 void SaveFormControlObjects(XclExpObjList& rList, XclExpXmlStream& rStrm)
277 {
278     bool hasControls = false;
279     for (const auto& rxObj : rList)
280     {
281         if (IsFormControlObject(rxObj.get()))
282         {
283             hasControls = true;
284             break;
285         }
286     }
287 
288     if (!hasControls)
289     {
290         return;
291     }
292 
293     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
294 
295     rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent),
296         FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)).toUtf8());
297     rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x14");
298     rWorksheet->startElement(XML_controls);
299 
300     for (const auto& rxObj : rList)
301     {
302         if (IsFormControlObject(rxObj.get()))
303         {
304             XclExpTbxControlObj* pXclExpTbxControlObj = dynamic_cast<XclExpTbxControlObj*>(rxObj.get());
305             if (pXclExpTbxControlObj)
306             {
307                 const OUString aIdFormControlPr = pXclExpTbxControlObj->SaveControlPropertiesXml(rStrm);
308                 pXclExpTbxControlObj->SaveSheetXml(rStrm, aIdFormControlPr);
309             }
310         }
311     }
312 
313     rWorksheet->endElement(XML_controls);
314     rWorksheet->endElement(FSNS(XML_mc, XML_Choice));
315     rWorksheet->endElement(FSNS(XML_mc, XML_AlternateContent));
316 }
317 
SaveVmlObjects(XclExpObjList & rList,XclExpXmlStream & rStrm)318 void SaveVmlObjects( XclExpObjList& rList, XclExpXmlStream& rStrm )
319 {
320     if( GetVmlObjectCount( rList ) == 0 )
321         return;
322 
323     sal_Int32 nDrawing = drawingml::DrawingML::getNewVMLUniqueId();
324     OUString sId;
325     sax_fastparser::FSHelperPtr pVmlDrawing = rStrm.CreateOutputStream(
326             XclXmlUtils::GetStreamName( "xl/", "drawings/vmlDrawing", nDrawing ),
327             XclXmlUtils::GetStreamName( "../", "drawings/vmlDrawing", nDrawing ),
328             rStrm.GetCurrentStream()->getOutputStream(),
329             "application/vnd.openxmlformats-officedocument.vmlDrawing",
330             oox::getRelationship(Relationship::VMLDRAWING),
331             &sId );
332 
333     rStrm.GetCurrentStream()->singleElement(XML_legacyDrawing, FSNS(XML_r, XML_id), sId.toUtf8());
334 
335     rStrm.PushStream( pVmlDrawing );
336     pVmlDrawing->startElement( XML_xml,
337             FSNS(XML_xmlns, XML_v),   rStrm.getNamespaceURL(OOX_NS(vml)).toUtf8(),
338             FSNS(XML_xmlns, XML_o),   rStrm.getNamespaceURL(OOX_NS(vmlOffice)).toUtf8(),
339             FSNS(XML_xmlns, XML_x),   rStrm.getNamespaceURL(OOX_NS(vmlExcel)).toUtf8(),
340             FSNS(XML_xmlns, XML_w10), rStrm.getNamespaceURL(OOX_NS(vmlWord)).toUtf8() );
341 
342     for ( const auto& rxObj : rList )
343     {
344         if( !IsVmlObject( rxObj.get() ) )
345             continue;
346         rxObj->SaveXml( rStrm );
347     }
348 
349     pVmlDrawing->endElement( XML_xml );
350 
351     rStrm.PopStream();
352 }
353 
354 }
355 
SaveXml(XclExpXmlStream & rStrm)356 void XclExpObjList::SaveXml( XclExpXmlStream& rStrm )
357 {
358     if( pSolverContainer )
359         pSolverContainer->SaveXml( rStrm );
360 
361     if( maObjs.empty())
362         return;
363 
364     SaveDrawingMLObjects( *this, rStrm );
365     SaveVmlObjects( *this, rStrm );
366     SaveFormControlObjects( *this, rStrm );
367 }
368 
369 // --- class XclObj --------------------------------------------------
370 
XclObj(XclExpObjectManager & rObjMgr,sal_uInt16 nObjType,bool bOwnEscher)371 XclObj::XclObj( XclExpObjectManager& rObjMgr, sal_uInt16 nObjType, bool bOwnEscher ) :
372     XclExpRecord( EXC_ID_OBJ, 26 ),
373     mrEscherEx( rObjMgr.GetEscherEx() ),
374     mnObjType( nObjType ),
375     nObjId(0),
376     nGrbit( 0x6011 ),   // AutoLine, AutoFill, Printable, Locked
377     mnScTab(0),
378     bFirstOnSheet( !rObjMgr.HasObj() ),
379     mbOwnEscher( bOwnEscher )
380 {
381     //! first object continues the first MSODRAWING record
382     if ( bFirstOnSheet )
383         pMsodrawing = rObjMgr.GetMsodrawingPerSheet();
384     else
385         pMsodrawing = new XclExpMsoDrawing( mrEscherEx );
386 }
387 
~XclObj()388 XclObj::~XclObj()
389 {
390     if ( !bFirstOnSheet )
391         delete pMsodrawing;
392     pClientTextbox.reset();
393     pTxo.reset();
394 }
395 
ImplWriteAnchor(const SdrObject * pSdrObj,const tools::Rectangle * pChildAnchor)396 void XclObj::ImplWriteAnchor( const SdrObject* pSdrObj, const tools::Rectangle* pChildAnchor )
397 {
398     if( pChildAnchor )
399     {
400         mrEscherEx.AddChildAnchor( *pChildAnchor );
401     }
402     else if( pSdrObj )
403     {
404         std::unique_ptr< XclExpDffAnchorBase > xDffAnchor( mrEscherEx.CreateDffAnchor( *pSdrObj ) );
405         xDffAnchor->WriteDffData( mrEscherEx );
406     }
407 }
408 
SetEscherShapeType(sal_uInt16 nType)409 void XclObj::SetEscherShapeType( sal_uInt16 nType )
410 {
411 //ToDo: what about the other defined or... types?
412     switch ( nType )
413     {
414         case ESCHER_ShpInst_Line :
415             mnObjType = EXC_OBJTYPE_LINE;
416         break;
417         case ESCHER_ShpInst_Rectangle :
418         case ESCHER_ShpInst_RoundRectangle :
419             mnObjType = EXC_OBJTYPE_RECTANGLE;
420         break;
421         case ESCHER_ShpInst_Ellipse :
422             mnObjType = EXC_OBJTYPE_OVAL;
423         break;
424         case ESCHER_ShpInst_Arc :
425             mnObjType = EXC_OBJTYPE_ARC;
426         break;
427         case ESCHER_ShpInst_TextBox :
428             mnObjType = EXC_OBJTYPE_TEXT;
429         break;
430         case ESCHER_ShpInst_PictureFrame :
431             mnObjType = EXC_OBJTYPE_PICTURE;
432         break;
433         default:
434             mnObjType = EXC_OBJTYPE_DRAWING;
435     }
436 }
437 
SetText(const XclExpRoot & rRoot,const SdrTextObj & rObj)438 void XclObj::SetText( const XclExpRoot& rRoot, const SdrTextObj& rObj )
439 {
440     OSL_ENSURE( !pClientTextbox, "XclObj::SetText: already set" );
441     if ( !pClientTextbox )
442     {
443         mrEscherEx.UpdateDffFragmentEnd();
444         pClientTextbox.reset( new XclExpMsoDrawing( mrEscherEx ) );
445         mrEscherEx.AddAtom( 0, ESCHER_ClientTextbox );    // TXO record
446         mrEscherEx.UpdateDffFragmentEnd();
447         pTxo.reset( new XclTxo( rRoot, rObj ) );
448     }
449 }
450 
WriteBody(XclExpStream & rStrm)451 void XclObj::WriteBody( XclExpStream& rStrm )
452 {
453     OSL_ENSURE( mnObjType != EXC_OBJTYPE_UNKNOWN, "XclObj::WriteBody - unknown type" );
454 
455     // create a substream to be able to create subrecords
456     SvMemoryStream aMemStrm;
457     std::optional< XclExpStream > pXclStrm( std::in_place, aMemStrm, rStrm.GetRoot() );
458 
459     // write the ftCmo subrecord
460     pXclStrm->StartRecord( EXC_ID_OBJCMO, 18 );
461     *pXclStrm << mnObjType << nObjId << nGrbit;
462     pXclStrm->WriteZeroBytes( 12 );
463     pXclStrm->EndRecord();
464 
465     // write other subrecords
466     WriteSubRecs( *pXclStrm );
467 
468     // write the ftEnd subrecord
469     pXclStrm->StartRecord( EXC_ID_OBJEND, 0 );
470     pXclStrm->EndRecord();
471 
472     // copy the data to the OBJ record
473     pXclStrm.reset();
474     aMemStrm.Seek( 0 );
475     rStrm.CopyFromStream( aMemStrm );
476 }
477 
Save(XclExpStream & rStrm)478 void XclObj::Save( XclExpStream& rStrm )
479 {
480     // MSODRAWING record (msofbtSpContainer)
481     if ( !bFirstOnSheet )
482         pMsodrawing->Save( rStrm );
483 
484     // OBJ
485     XclExpRecord::Save( rStrm );
486 
487     // second MSODRAWING record and TXO and CONTINUE records
488     SaveTextRecs( rStrm );
489 }
490 
WriteSubRecs(XclExpStream & rStrm)491 void XclObj::WriteSubRecs( XclExpStream& rStrm )
492 {
493     if( mnObjType != EXC_OBJTYPE_NOTE )
494         return;
495 
496     // FtNts subrecord
497     AddRecSize( 26 );
498     // ft, cb
499     rStrm << EXC_ID_OBJNTS << sal_uInt16(0x0016);
500     sal_uInt8 aGUID[16];
501     rtl_createUuid( aGUID, nullptr, false );
502     // guid
503     rStrm.SetSliceSize( 16 );
504     for( int i = 0; i < 16; i++ )
505         rStrm << aGUID[i];
506     rStrm.SetSliceSize( 0 );
507     // fSharedNote
508     rStrm << sal_uInt16(0);
509     // unused
510     rStrm.WriteZeroBytes( 4 );
511 }
512 
SaveTextRecs(XclExpStream & rStrm)513 void XclObj::SaveTextRecs( XclExpStream& rStrm )
514 {
515     // MSODRAWING record (msofbtClientTextbox)
516     if ( pClientTextbox )
517         pClientTextbox->Save( rStrm );
518     // TXO and CONTINUE records
519     if ( pTxo )
520         pTxo->Save( rStrm );
521 }
522 
523 // --- class XclObjComment ------------------------------------------
524 
525 // tdf#118662 static helper to allow single function access as friend in SdrCaptionObj
setSuppressGetBitmapFromXclObjComment(SdrCaptionObj * pSdrCaptionObj,bool bValue)526 void setSuppressGetBitmapFromXclObjComment(SdrCaptionObj* pSdrCaptionObj, bool bValue)
527 {
528     if(nullptr != pSdrCaptionObj)
529     {
530         pSdrCaptionObj->setSuppressGetBitmap(bValue);
531     }
532 }
533 
XclObjComment(XclExpObjectManager & rObjMgr,const tools::Rectangle & rRect,const EditTextObject & rEditObj,SdrCaptionObj * pCaption,bool bVisible,const ScAddress & rAddress,const tools::Rectangle & rFrom,const tools::Rectangle & rTo)534 XclObjComment::XclObjComment( XclExpObjectManager& rObjMgr, const tools::Rectangle& rRect, const EditTextObject& rEditObj, SdrCaptionObj* pCaption, bool bVisible, const ScAddress& rAddress, const tools::Rectangle &rFrom, const tools::Rectangle &rTo ) :
535     XclObj( rObjMgr, EXC_OBJTYPE_NOTE, true )
536             , maScPos( rAddress )
537             , mpCaption( pCaption )
538             , mbVisible( bVisible )
539             , maFrom ( rFrom )
540             , maTo ( rTo )
541 {
542     // tdf#118662 due to no longer cloning the SdrCaptionObj an old 'hack' using the
543     // fact that no Graphics gets created when a SdrObject is not inserted in a SdrPage
544     // does not work anymore. In SvxShape::GetBitmap that info was used, and here the
545     // SdrCaptionObj was cloned for the only reason to have one not added to a SdrPage.
546     // To emulate old behaviour, use a boolean flag at the SdrCaptionObj.
547     setSuppressGetBitmapFromXclObjComment(mpCaption, true);
548 
549     ProcessEscherObj( rObjMgr.GetRoot(), rRect, pCaption, bVisible);
550     // TXO
551     pTxo .reset(new XclTxo( rObjMgr.GetRoot(), rEditObj, pCaption ));
552 }
553 
lcl_FillProps(EscherPropertyContainer & rPropOpt,SdrObject * pCaption,bool bVisible)554 static void lcl_FillProps( EscherPropertyContainer& rPropOpt, SdrObject* pCaption, bool bVisible )
555 {
556     if( pCaption )
557     {
558         Reference< XShape > aXShape = GetXShapeForSdrObject( pCaption );
559         Reference< XPropertySet > aXPropSet( aXShape, UNO_QUERY );
560         if( aXPropSet.is() )
561         {
562             rPropOpt.CreateFillProperties( aXPropSet, true);
563 
564             rPropOpt.AddOpt( ESCHER_Prop_lTxid, 0 );                        // undocumented
565             rPropOpt.AddOpt( 0x0158, 0x00000000 );                          // undocumented
566 
567             sal_uInt32 nValue = 0;
568             if( !rPropOpt.GetOpt( ESCHER_Prop_FitTextToShape, nValue ) )
569                 rPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 );      // bool field
570 
571             // Maybe the colour is the same as the 'ToolTip' System colour, but the tooltip
572             // colour shouldn't have influence on the fill colour of the exported shape
573             if( !rPropOpt.GetOpt( ESCHER_Prop_fillColor, nValue ) )
574                  rPropOpt.AddOpt( ESCHER_Prop_fillColor, 0x08000050 );
575             if( !rPropOpt.GetOpt( ESCHER_Prop_fillBackColor, nValue ) )
576                 rPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x08000050 );
577             if( !rPropOpt.GetOpt( ESCHER_Prop_fNoFillHitTest, nValue ) )
578                 rPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00110010 );      // bool field
579             if( !rPropOpt.GetOpt( ESCHER_Prop_shadowColor, nValue ) )
580                 rPropOpt.AddOpt( ESCHER_Prop_shadowColor, 0x00000000 );
581             if( !rPropOpt.GetOpt( ESCHER_Prop_fshadowObscured, nValue ) )       // bool field
582                 rPropOpt.AddOpt( ESCHER_Prop_fshadowObscured, 0x00030003 );     // bool field
583         }
584     }
585 
586     sal_uInt32 nFlags = 0x000A0000;
587     ::set_flag( nFlags, sal_uInt32(2), !bVisible );
588     rPropOpt.AddOpt( ESCHER_Prop_fPrint, nFlags );                  // bool field
589 }
590 
ProcessEscherObj(const XclExpRoot & rRoot,const tools::Rectangle & rRect,SdrObject * pCaption,const bool bVisible)591 void XclObjComment::ProcessEscherObj( const XclExpRoot& rRoot, const tools::Rectangle& rRect, SdrObject* pCaption, const bool bVisible )
592 {
593     EscherPropertyContainer aPropOpt;
594 
595     lcl_FillProps( aPropOpt, pCaption, bVisible );
596 
597     nGrbit = 0;     // all off: AutoLine, AutoFill, Printable, Locked
598     mrEscherEx.OpenContainer( ESCHER_SpContainer );
599     mrEscherEx.AddShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty );
600     aPropOpt.Commit( mrEscherEx.GetStream() );
601 
602     XclExpDffNoteAnchor( rRoot, rRect ).WriteDffData( mrEscherEx );
603 
604     mrEscherEx.AddAtom( 0, ESCHER_ClientData );                        // OBJ record
605     mrEscherEx.UpdateDffFragmentEnd();
606 
607     //! Be sure to construct the MSODRAWING ClientTextbox record _after_ the
608     //! base OBJ's MSODRAWING record Escher data is completed.
609     pClientTextbox.reset( new XclExpMsoDrawing( mrEscherEx ) );
610     mrEscherEx.AddAtom( 0, ESCHER_ClientTextbox );    // TXO record
611     mrEscherEx.UpdateDffFragmentEnd();
612     mrEscherEx.CloseContainer();   // ESCHER_SpContainer
613 }
614 
~XclObjComment()615 XclObjComment::~XclObjComment()
616 {
617     // tdf#118662 reset flag
618     setSuppressGetBitmapFromXclObjComment(mpCaption, false);
619 }
620 
Save(XclExpStream & rStrm)621 void XclObjComment::Save( XclExpStream& rStrm )
622 {
623     // content of this record
624     XclObj::Save( rStrm );
625 }
626 
627 namespace {
628 
629 class VmlCommentExporter : public VMLExport
630 {
631     ScAddress           maScPos;
632     SdrCaptionObj*      mpCaption;
633     bool                mbVisible;
634     tools::Rectangle           maFrom;
635     tools::Rectangle           maTo;
636 
637 public:
638                         VmlCommentExporter ( const sax_fastparser::FSHelperPtr& p, const ScAddress& aScPos, SdrCaptionObj* pCaption, bool bVisible, const tools::Rectangle &aFrom, const tools::Rectangle &aTo );
639 protected:
640     virtual void        Commit( EscherPropertyContainer& rProps, const tools::Rectangle& rRect ) override;
641     using VMLExport::StartShape;
642     virtual sal_Int32   StartShape() override;
643     using VMLExport::EndShape;
644     virtual void        EndShape( sal_Int32 nShapeElement ) override;
645 };
646 
647 }
648 
VmlCommentExporter(const sax_fastparser::FSHelperPtr & p,const ScAddress & aScPos,SdrCaptionObj * pCaption,bool bVisible,const tools::Rectangle & aFrom,const tools::Rectangle & aTo)649 VmlCommentExporter::VmlCommentExporter( const sax_fastparser::FSHelperPtr& p, const ScAddress& aScPos, SdrCaptionObj* pCaption,
650                                         bool bVisible, const tools::Rectangle &aFrom, const tools::Rectangle &aTo )
651     : VMLExport( p )
652     , maScPos( aScPos )
653     , mpCaption( pCaption )
654     , mbVisible( bVisible )
655     , maFrom ( aFrom )
656     , maTo ( aTo )
657 {
658 }
659 
Commit(EscherPropertyContainer & rProps,const tools::Rectangle & rRect)660 void VmlCommentExporter::Commit( EscherPropertyContainer& rProps, const tools::Rectangle& rRect )
661 {
662     lcl_FillProps( rProps, mpCaption, mbVisible );
663     rProps.AddOpt( ESCHER_Prop_fHidden, sal_uInt32(mbVisible) ); // bool field
664 
665     // shadow property value for comment ( set in lcl_FillProps [*] ) has been
666     // overwritten by new value ( 0x20000 ) in the generic part of the export
667     // ( see  EscherPropertyContainer::CreateShadowProperties )
668     // Safer option here is to just force the needed value here for oox vml
669     // export alone ( and avoid potential problems with binary export )
670     // #TODO investigate value of ESCHER_Prop_fshadowObscured generally
671     // in binary export ( if indeed this value is good for binary export )
672     // we can change the heuristics and/or initialisation path and get
673     // rid of line below.
674     // [*] lcl_FillProps seems to be called twice when exporting to xlsx
675     // once from XclObjComment::ProcessEscherObj #TODO look into that also
676     rProps.AddOpt( ESCHER_Prop_fshadowObscured, 0x00030003 ); // force value for comments
677 
678     VMLExport::Commit( rProps, rRect );
679 }
680 
StartShape()681 sal_Int32 VmlCommentExporter::StartShape()
682 {
683     AddShapeAttribute( XML_type, "#_x0000_t202" );
684 
685     sal_Int32 nId = VMLExport::StartShape();
686 
687     return nId;
688 }
689 
lcl_GetHorizAlignFromItemSetChar(const SfxItemSet & rItemSet)690 static const char* lcl_GetHorizAlignFromItemSetChar(const SfxItemSet& rItemSet)
691 {
692     switch (rItemSet.Get(EE_PARA_JUST).GetAdjust())
693     {
694         case SvxAdjust::Center:
695             return "Center";
696         case SvxAdjust::Right:
697             return "Right";
698         case SvxAdjust::Block:
699             return "Justify";
700         default:
701             return "Left";
702     }
703 }
704 
lcl_GetVertAlignFromItemSetChar(const SfxItemSet & rItemSet)705 static const char* lcl_GetVertAlignFromItemSetChar( const SfxItemSet& rItemSet )
706 {
707     switch( rItemSet.Get( SDRATTR_TEXT_VERTADJUST ).GetValue() )
708     {
709         case SDRTEXTVERTADJUST_CENTER:
710             return "Center";
711         case SDRTEXTVERTADJUST_BOTTOM:
712             return "Bottom";
713         case SDRTEXTVERTADJUST_BLOCK:
714             return "Justify";
715         case SDRTEXTVERTADJUST_TOP:
716         default:
717             return "Top";
718     }
719 }
720 
EndShape(sal_Int32 nShapeElement)721 void VmlCommentExporter::EndShape( sal_Int32 nShapeElement )
722 {
723     char pAnchor[100];
724     sax_fastparser::FSHelperPtr pVmlDrawing = GetFS();
725     snprintf( pAnchor, 100, "%" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64,
726                   sal_Int64(maFrom.Left()), sal_Int64(maFrom.Top()), sal_Int64(maFrom.Right()), sal_Int64(maFrom.Bottom()),
727                   sal_Int64(maTo.Left()), sal_Int64(maTo.Top()), sal_Int64(maTo.Right()), sal_Int64(maTo.Bottom()) );
728 
729     // Getting comment text alignments
730     const char* pVertAlign = lcl_GetVertAlignFromItemSetChar(mpCaption->GetMergedItemSet());
731     const char* pHorizAlign = lcl_GetHorizAlignFromItemSetChar(mpCaption->GetMergedItemSet());
732 
733     pVmlDrawing->startElement(FSNS(XML_x, XML_ClientData), XML_ObjectType, "Note");
734     pVmlDrawing->singleElement(FSNS(XML_x, XML_MoveWithCells));
735     pVmlDrawing->singleElement(FSNS(XML_x, XML_SizeWithCells));
736     XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_Anchor ), pAnchor );
737     XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_AutoFill ), "False" );
738     XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_TextVAlign ), pVertAlign );
739     XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_TextHAlign ), pHorizAlign );
740     XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_Row ), maScPos.Row() );
741     XclXmlUtils::WriteElement( pVmlDrawing, FSNS(XML_x, XML_Column), sal_Int32(maScPos.Col()));
742     if(mbVisible)
743         pVmlDrawing->singleElement(FSNS(XML_x, XML_Visible));
744     pVmlDrawing->endElement( FSNS( XML_x, XML_ClientData ) );
745 
746     VMLExport::EndShape( nShapeElement );
747 }
748 
SaveXml(XclExpXmlStream & rStrm)749 void XclObjComment::SaveXml( XclExpXmlStream& rStrm )
750 {
751     VmlCommentExporter aCommentExporter( rStrm.GetCurrentStream(), maScPos, mpCaption, mbVisible, maFrom, maTo );
752     aCommentExporter.AddSdrObject( *mpCaption );
753 }
754 
755 // --- class XclObjDropDown ------------------------------------------
756 
XclObjDropDown(XclExpObjectManager & rObjMgr,const ScAddress & rPos,bool bFilt)757 XclObjDropDown::XclObjDropDown( XclExpObjectManager& rObjMgr, const ScAddress& rPos, bool bFilt ) :
758     XclObj( rObjMgr, EXC_OBJTYPE_DROPDOWN, true ),
759     bIsFiltered( bFilt )
760 {
761     SetLocked( true );
762     SetPrintable( false );
763     SetAutoFill( true );
764     SetAutoLine( false );
765     nGrbit |= 0x0100;   // undocumented
766     mrEscherEx.OpenContainer( ESCHER_SpContainer );
767     mrEscherEx.AddShape( ESCHER_ShpInst_HostControl, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty );
768     EscherPropertyContainer aPropOpt;
769     aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x01040104 ); // bool field
770     aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 );      // bool field
771     aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00010000 );      // bool field
772     aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080000 );     // bool field
773     aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x000A0000 );              // bool field
774     aPropOpt.Commit( mrEscherEx.GetStream() );
775 
776     XclExpDffDropDownAnchor( rObjMgr.GetRoot(), rPos ).WriteDffData( mrEscherEx );
777 
778     mrEscherEx.AddAtom( 0, ESCHER_ClientData );                        // OBJ record
779     mrEscherEx.UpdateDffFragmentEnd();
780     mrEscherEx.CloseContainer();   // ESCHER_SpContainer
781 
782     // old size + ftSbs + ftLbsData
783     AddRecSize( 24 + 20 );
784 }
785 
~XclObjDropDown()786 XclObjDropDown::~XclObjDropDown()
787 {
788 }
789 
WriteSubRecs(XclExpStream & rStrm)790 void XclObjDropDown::WriteSubRecs( XclExpStream& rStrm )
791 {
792     // ftSbs subrecord - Scroll bars (dummy)
793     rStrm.StartRecord( EXC_ID_OBJSBS, 20 );
794     rStrm.WriteZeroBytes( 20 );
795     rStrm.EndRecord();
796 
797     // ftLbsData subrecord - Listbox data
798     sal_uInt16 nDropDownFlags = 0;
799     ::insert_value( nDropDownFlags, EXC_OBJ_DROPDOWN_SIMPLE, 0, 2 );
800     ::set_flag( nDropDownFlags, EXC_OBJ_DROPDOWN_FILTERED, bIsFiltered );
801     rStrm.StartRecord( EXC_ID_OBJLBSDATA, 16 );
802     rStrm   << sal_uInt32(0) << sal_uInt16(0) << sal_uInt16(0x0301) << sal_uInt16(0)
803             << nDropDownFlags << sal_uInt16( 20 ) << sal_uInt16( 130 );
804     rStrm.EndRecord();
805 }
806 
807 // --- class XclTxo --------------------------------------------------
808 
lcl_GetHorAlignFromItemSet(const SfxItemSet & rItemSet)809 static sal_uInt8 lcl_GetHorAlignFromItemSet( const SfxItemSet& rItemSet )
810 {
811     sal_uInt8 nHorAlign = EXC_OBJ_HOR_LEFT;
812 
813     switch( rItemSet.Get( EE_PARA_JUST ).GetAdjust() )
814     {
815         case SvxAdjust::Left:   nHorAlign = EXC_OBJ_HOR_LEFT;      break;
816         case SvxAdjust::Center: nHorAlign = EXC_OBJ_HOR_CENTER;    break;
817         case SvxAdjust::Right:  nHorAlign = EXC_OBJ_HOR_RIGHT;     break;
818         case SvxAdjust::Block:  nHorAlign = EXC_OBJ_HOR_JUSTIFY;   break;
819         default:;
820     }
821     return nHorAlign;
822 }
823 
lcl_GetVerAlignFromItemSet(const SfxItemSet & rItemSet)824 static sal_uInt8 lcl_GetVerAlignFromItemSet( const SfxItemSet& rItemSet )
825 {
826     sal_uInt8 nVerAlign = EXC_OBJ_VER_TOP;
827 
828     switch( rItemSet.Get( SDRATTR_TEXT_VERTADJUST ).GetValue() )
829     {
830         case SDRTEXTVERTADJUST_TOP:     nVerAlign = EXC_OBJ_VER_TOP;       break;
831         case SDRTEXTVERTADJUST_CENTER:  nVerAlign = EXC_OBJ_VER_CENTER;    break;
832         case SDRTEXTVERTADJUST_BOTTOM:  nVerAlign = EXC_OBJ_VER_BOTTOM;    break;
833         case SDRTEXTVERTADJUST_BLOCK:   nVerAlign = EXC_OBJ_VER_JUSTIFY;   break;
834         default:;
835     }
836     return nVerAlign;
837 }
838 
XclTxo(const OUString & rString,sal_uInt16 nFontIx)839 XclTxo::XclTxo( const OUString& rString, sal_uInt16 nFontIx ) :
840     mpString( std::make_shared<XclExpString>( rString ) ),
841     mnRotation( EXC_OBJ_ORIENT_NONE ),
842     mnHorAlign( EXC_OBJ_HOR_LEFT ),
843     mnVerAlign( EXC_OBJ_VER_TOP )
844 {
845     if( mpString->Len() )
846     {
847         // If there is text, Excel *needs* the 2nd CONTINUE record with at least two format runs
848         mpString->AppendFormat( 0, nFontIx );
849         mpString->AppendFormat( mpString->Len(), EXC_FONT_APP );
850     }
851 }
852 
XclTxo(const XclExpRoot & rRoot,const SdrTextObj & rTextObj)853 XclTxo::XclTxo( const XclExpRoot& rRoot, const SdrTextObj& rTextObj ) :
854     mpString( XclExpStringHelper::CreateString( rRoot, rTextObj ) ),
855     mnRotation( EXC_OBJ_ORIENT_NONE ),
856     mnHorAlign( EXC_OBJ_HOR_LEFT ),
857     mnVerAlign( EXC_OBJ_VER_TOP )
858 {
859     // additional alignment and orientation items
860     const SfxItemSet& rItemSet = rTextObj.GetMergedItemSet();
861 
862     // horizontal alignment
863     SetHorAlign( lcl_GetHorAlignFromItemSet( rItemSet ) );
864 
865     // vertical alignment
866     SetVerAlign( lcl_GetVerAlignFromItemSet( rItemSet ) );
867 
868     // rotation
869     Degree100 nAngle = rTextObj.GetRotateAngle();
870     if( (4500_deg100 < nAngle) && (nAngle < 13500_deg100) )
871         mnRotation = EXC_OBJ_ORIENT_90CCW;
872     else if( (22500_deg100 < nAngle) && (nAngle < 31500_deg100) )
873         mnRotation = EXC_OBJ_ORIENT_90CW;
874     else
875         mnRotation = EXC_OBJ_ORIENT_NONE;
876 }
877 
XclTxo(const XclExpRoot & rRoot,const EditTextObject & rEditObj,SdrObject * pCaption)878 XclTxo::XclTxo( const XclExpRoot& rRoot, const EditTextObject& rEditObj, SdrObject* pCaption ) :
879     mpString( XclExpStringHelper::CreateString( rRoot, rEditObj ) ),
880     mnRotation( EXC_OBJ_ORIENT_NONE ),
881     mnHorAlign( EXC_OBJ_HOR_LEFT ),
882     mnVerAlign( EXC_OBJ_VER_TOP )
883 {
884     if(!pCaption)
885         return;
886 
887     // Excel has one alignment per NoteObject while Calc supports
888     // one alignment per paragraph - use the first paragraph
889     // alignment (if set) as our overall alignment.
890     OUString aParaText( rEditObj.GetText( 0 ) );
891     if( !aParaText.isEmpty() )
892     {
893         const SfxItemSet& aSet( rEditObj.GetParaAttribs( 0));
894         const SfxPoolItem* pItem = nullptr;
895         if( aSet.GetItemState( EE_PARA_JUST, true, &pItem ) == SfxItemState::SET )
896         {
897             SvxAdjust eEEAlign = static_cast< const SvxAdjustItem& >( *pItem ).GetAdjust();
898             pCaption->SetMergedItem( SvxAdjustItem( eEEAlign, EE_PARA_JUST ) );
899         }
900     }
901     const SfxItemSet& rItemSet = pCaption->GetMergedItemSet();
902 
903     // horizontal alignment
904     SetHorAlign( lcl_GetHorAlignFromItemSet( rItemSet ) );
905 
906     // vertical alignment
907     SetVerAlign( lcl_GetVerAlignFromItemSet( rItemSet ) );
908 
909     // orientation alignment
910     const SvxWritingModeItem& rItem = rItemSet.Get( SDRATTR_TEXTDIRECTION );
911     if( rItem.GetValue() == css::text::WritingMode_TB_RL )
912         mnRotation = EXC_OBJ_ORIENT_90CW;
913 }
914 
SaveCont(XclExpStream & rStrm)915 void XclTxo::SaveCont( XclExpStream& rStrm )
916 {
917     OSL_ENSURE( mpString, "XclTxo::SaveCont - missing string" );
918 
919     // #i96858# do not save existing string formatting if text is empty
920     sal_uInt16 nRunLen = mpString->IsEmpty() ? 0 : (8 * mpString->GetFormatsCount());
921     // alignment
922     sal_uInt16 nFlags = 0;
923     ::insert_value( nFlags, mnHorAlign, 1, 3 );
924     ::insert_value( nFlags, mnVerAlign, 4, 3 );
925 
926     rStrm << nFlags << mnRotation;
927     rStrm.WriteZeroBytes( 6 );
928     rStrm << mpString->Len() << nRunLen << sal_uInt32( 0 );
929 }
930 
Save(XclExpStream & rStrm)931 void XclTxo::Save( XclExpStream& rStrm )
932 {
933     // Write the TXO part
934     ExcRecord::Save( rStrm );
935 
936     // CONTINUE records are only written if there is some text
937     if( mpString->IsEmpty() )
938         return;
939 
940     // CONTINUE for character array
941     rStrm.StartRecord( EXC_ID_CONT, mpString->GetBufferSize() + 1 );
942     rStrm << static_cast< sal_uInt8 >( mpString->GetFlagField() & EXC_STRF_16BIT ); // only Unicode flag
943     mpString->WriteBuffer( rStrm );
944     rStrm.EndRecord();
945 
946     // CONTINUE for formatting runs
947     rStrm.StartRecord( EXC_ID_CONT, 8 * mpString->GetFormatsCount() );
948     const XclFormatRunVec& rFormats = mpString->GetFormats();
949     for( const auto& rFormat : rFormats )
950         rStrm << rFormat.mnChar << rFormat.mnFontIdx << sal_uInt32( 0 );
951     rStrm.EndRecord();
952 }
953 
GetNum() const954 sal_uInt16 XclTxo::GetNum() const
955 {
956     return EXC_ID_TXO;
957 }
958 
GetLen() const959 std::size_t XclTxo::GetLen() const
960 {
961     return 18;
962 }
963 
964 // --- class XclObjOle -------------------------------------------
965 
XclObjOle(XclExpObjectManager & rObjMgr,const SdrObject & rObj)966 XclObjOle::XclObjOle( XclExpObjectManager& rObjMgr, const SdrObject& rObj ) :
967     XclObj( rObjMgr, EXC_OBJTYPE_PICTURE ),
968     rOleObj( rObj ),
969     pRootStorage( rObjMgr.GetRoot().GetRootStorage().get() )
970 {
971 }
972 
~XclObjOle()973 XclObjOle::~XclObjOle()
974 {
975 }
976 
WriteSubRecs(XclExpStream & rStrm)977 void XclObjOle::WriteSubRecs( XclExpStream& rStrm )
978 {
979     // write only as embedded, not linked
980     OUString        aStorageName( "MBD" );
981     char        aBuf[ sizeof(sal_uInt32) * 2 + 1 ];
982     // FIXME Eeek! Is this just a way to get a unique id?
983     sal_uInt32          nPictureId = sal_uInt32(reinterpret_cast<sal_uIntPtr>(this) >> 2);
984     sprintf( aBuf, "%08X", static_cast< unsigned int >( nPictureId ) );
985     aStorageName += OUString::createFromAscii(aBuf);
986     tools::SvRef<SotStorage>    xOleStg = pRootStorage->OpenSotStorage( aStorageName );
987     if( !xOleStg.is() )
988         return;
989 
990     uno::Reference < embed::XEmbeddedObject > xObj( static_cast<const SdrOle2Obj&>(rOleObj).GetObjRef() );
991     if ( !xObj.is() )
992         return;
993 
994     // set version to "old" version, because it must be
995     // saved in MS notation.
996     sal_uInt32                  nFl = 0;
997     const SvtFilterOptions& rFltOpts = SvtFilterOptions::Get();
998     if( rFltOpts.IsMath2MathType() )
999         nFl |= OLE_STARMATH_2_MATHTYPE;
1000 
1001     if( rFltOpts.IsWriter2WinWord() )
1002         nFl |= OLE_STARWRITER_2_WINWORD;
1003 
1004     if( rFltOpts.IsCalc2Excel() )
1005         nFl |= OLE_STARCALC_2_EXCEL;
1006 
1007     if( rFltOpts.IsImpress2PowerPoint() )
1008         nFl |= OLE_STARIMPRESS_2_POWERPOINT;
1009 
1010     SvxMSExportOLEObjects   aOLEExpFilt( nFl );
1011     aOLEExpFilt.ExportOLEObject( xObj, *xOleStg );
1012 
1013     // OBJCF subrecord, undocumented as usual
1014     rStrm.StartRecord( EXC_ID_OBJCF, 2 );
1015     rStrm << sal_uInt16(0x0002);
1016     rStrm.EndRecord();
1017 
1018     // OBJFLAGS subrecord, undocumented as usual
1019     rStrm.StartRecord( EXC_ID_OBJFLAGS, 2 );
1020     sal_uInt16 nFlags = EXC_OBJ_PIC_MANUALSIZE;
1021     ::set_flag( nFlags, EXC_OBJ_PIC_SYMBOL, static_cast<const SdrOle2Obj&>(rOleObj).GetAspect() == embed::Aspects::MSOLE_ICON );
1022     rStrm << nFlags;
1023     rStrm.EndRecord();
1024 
1025     // OBJPICTFMLA subrecord, undocumented as usual
1026     XclExpString aName( xOleStg->GetUserName() );
1027     sal_uInt16 nPadLen = static_cast<sal_uInt16>(aName.GetSize() & 0x01);
1028     sal_uInt16 nFmlaLen = static_cast< sal_uInt16 >( 12 + aName.GetSize() + nPadLen );
1029     sal_uInt16 nSubRecLen = nFmlaLen + 6;
1030 
1031     rStrm.StartRecord( EXC_ID_OBJPICTFMLA, nSubRecLen );
1032     rStrm   << nFmlaLen
1033             << sal_uInt16( 5 ) << sal_uInt32( 0 ) << sal_uInt8( 2 )
1034             << sal_uInt32( 0 ) << sal_uInt8( 3 )
1035             << aName;
1036     if( nPadLen )
1037         rStrm << sal_uInt8( 0 );       // pad byte
1038     rStrm << nPictureId;
1039     rStrm.EndRecord();
1040 }
1041 
Save(XclExpStream & rStrm)1042 void XclObjOle::Save( XclExpStream& rStrm )
1043 {
1044     // content of this record
1045     XclObj::Save( rStrm );
1046 }
1047 
1048 // --- class XclObjAny -------------------------------------------
1049 
XclObjAny(XclExpObjectManager & rObjMgr,const Reference<XShape> & rShape,ScDocument * pDoc)1050 XclObjAny::XclObjAny( XclExpObjectManager& rObjMgr, const Reference< XShape >& rShape, ScDocument* pDoc )
1051     : XclObj( rObjMgr, EXC_OBJTYPE_UNKNOWN )
1052     , mxShape( rShape )
1053     , mpDoc(pDoc)
1054 {
1055 }
1056 
~XclObjAny()1057 XclObjAny::~XclObjAny()
1058 {
1059 }
1060 
WriteSubRecs(XclExpStream & rStrm)1061 void XclObjAny::WriteSubRecs( XclExpStream& rStrm )
1062 {
1063     if( mnObjType == EXC_OBJTYPE_GROUP )
1064         // ftGmo subrecord
1065         rStrm << EXC_ID_OBJGMO << sal_uInt16(2) << sal_uInt16(0);
1066 }
1067 
Save(XclExpStream & rStrm)1068 void XclObjAny::Save( XclExpStream& rStrm )
1069 {
1070     if( mnObjType == EXC_OBJTYPE_GROUP )
1071         // old size + ftGmo
1072         AddRecSize( 6 );
1073 
1074     // content of this record
1075     XclObj::Save( rStrm );
1076 }
1077 
1078 // --- class ExcBof8_Base --------------------------------------------
1079 
ExcBof8_Base()1080 ExcBof8_Base::ExcBof8_Base()
1081 {
1082     nVers           = 0x0600;
1083     nRupBuild       = 0x0dbb;
1084     nRupYear        = 0x07cc;
1085 }
1086 
WriteFromTo(XclExpXmlStream & rStrm,const Reference<XShape> & rShape,SCTAB nTab)1087 void XclObjAny::WriteFromTo( XclExpXmlStream& rStrm, const Reference< XShape >& rShape, SCTAB nTab )
1088 {
1089     sax_fastparser::FSHelperPtr pDrawing = rStrm.GetCurrentStream();
1090 
1091     awt::Point  aTopLeft    = rShape->getPosition();
1092     awt::Size   aSize       = rShape->getSize();
1093 
1094     // There are a few cases where we must adjust these values
1095     // Do not adjust objects, which have rotation incorporated into their points
1096     // but report a rotation angle nevertheless.
1097     SdrObject* pObj = SdrObject::getSdrObjectFromXShape(rShape);
1098     if (pObj && pObj->GetObjIdentifier() != OBJ_LINE && pObj->GetObjIdentifier() != OBJ_PLIN
1099         && pObj->GetObjIdentifier() != OBJ_PATHLINE && pObj->GetObjIdentifier() != OBJ_FREELINE
1100         && pObj->GetObjIdentifier() != OBJ_PATHPLIN)
1101     {
1102         Degree100 nRotation = NormAngle36000(pObj->GetRotateAngle());
1103         if (nRotation)
1104         {
1105             sal_Int16 nHalfWidth = aSize.Width / 2;
1106             sal_Int16 nHalfHeight = aSize.Height / 2;
1107 
1108             const tools::Rectangle& aSnapRect(pObj->GetSnapRect()); // bounding box of the rotated shape
1109             aTopLeft.X = aSnapRect.getX() + (aSnapRect.GetWidth() / 2) - nHalfWidth;
1110             aTopLeft.Y = aSnapRect.getY() + (aSnapRect.GetHeight() / 2) - nHalfHeight;
1111 
1112             // MSO changes the anchor positions at these angles and that does an extra 90 degrees
1113             // rotation on our shapes, so we output it in such position that MSO
1114             // can draw this shape correctly.
1115             if ((nRotation > 4500_deg100 && nRotation <= 13500_deg100) || (nRotation > 22500_deg100 && nRotation <= 31500_deg100))
1116             {
1117                 aTopLeft.X = aTopLeft.X - nHalfHeight + nHalfWidth;
1118                 aTopLeft.Y = aTopLeft.Y - nHalfWidth + nHalfHeight;
1119 
1120                 std::swap(aSize.Width, aSize.Height);
1121             }
1122         }
1123     }
1124 
1125     tools::Rectangle   aLocation( aTopLeft.X, aTopLeft.Y, aTopLeft.X + aSize.Width, aTopLeft.Y + aSize.Height );
1126     ScRange     aRange      = rStrm.GetRoot().GetDoc().GetRange( nTab, aLocation );
1127     tools::Rectangle   aRangeRect  = rStrm.GetRoot().GetDoc().GetMMRect( aRange.aStart.Col(), aRange.aStart.Row(),
1128             aRange.aEnd.Col()-1, aRange.aEnd.Row()-1,
1129             nTab );
1130 
1131     pDrawing->startElement(FSNS(XML_xdr, XML_from));
1132     XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_col ), static_cast<sal_Int32>(aRange.aStart.Col()) );
1133     XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_colOff ),
1134             oox::drawingml::convertHmmToEmu( aLocation.Left() - aRangeRect.Left() ) );
1135     XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_row ), static_cast<sal_Int32>(aRange.aStart.Row()) );
1136     XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_rowOff ),
1137             oox::drawingml::convertHmmToEmu( aLocation.Top() - aRangeRect.Top() ) );
1138     pDrawing->endElement( FSNS( XML_xdr, XML_from ) );
1139 
1140     pDrawing->startElement(FSNS(XML_xdr, XML_to));
1141     XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_col ), static_cast<sal_Int32>(aRange.aEnd.Col()) );
1142     XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_colOff ),
1143             oox::drawingml::convertHmmToEmu( aLocation.Right() - aRangeRect.Right() ) );
1144     XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_row ), static_cast<sal_Int32>(aRange.aEnd.Row()) );
1145     XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_rowOff ),
1146             oox::drawingml::convertHmmToEmu( aLocation.Bottom() - aRangeRect.Bottom() ) );
1147     pDrawing->endElement( FSNS( XML_xdr, XML_to ) );
1148 }
1149 
WriteFromTo(XclExpXmlStream & rStrm,const XclObjAny & rObj)1150 void XclObjAny::WriteFromTo( XclExpXmlStream& rStrm, const XclObjAny& rObj )
1151 {
1152     WriteFromTo( rStrm, rObj.GetShape(), rObj.GetTab() );
1153 }
1154 
1155 static const char*
GetEditAs(const XclObjAny & rObj)1156 GetEditAs( const XclObjAny& rObj )
1157 {
1158     if( const SdrObject* pShape = EscherEx::GetSdrObject( rObj.GetShape() ) )
1159     {
1160         switch( ScDrawLayer::GetAnchorType( *pShape ) )
1161         {
1162             case SCA_CELL:
1163                 return "oneCell";
1164             case SCA_CELL_RESIZE:
1165                 return "twoCell";
1166             default:
1167             case SCA_PAGE:
1168                 break; // absolute
1169         }
1170     }
1171     return "absolute";
1172 }
1173 
1174 namespace {
1175 
parseRange(const OUString & rString,ScRange & rRange,const ScDocument & rDoc)1176 ScRefFlags parseRange(const OUString& rString, ScRange& rRange, const ScDocument& rDoc)
1177 {
1178     // start with the address convention set in the document
1179     formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
1180     ScRefFlags nResult = rRange.Parse(rString, rDoc, eConv);
1181     if ( nResult & ScRefFlags::VALID )
1182         return nResult;
1183 
1184     // try the default calc address convention
1185     nResult = rRange.Parse(rString, rDoc);
1186     if ( nResult & ScRefFlags::VALID )
1187         return nResult;
1188 
1189     // try excel a1
1190     nResult = rRange.Parse(rString, rDoc, formula::FormulaGrammar::CONV_XL_A1);
1191     if ( nResult & ScRefFlags::VALID )
1192         return nResult;
1193 
1194     // try r1c1
1195     return rRange.Parse(rString, rDoc, formula::FormulaGrammar::CONV_XL_R1C1);
1196 }
1197 
parseAddress(const OUString & rString,ScAddress & rAddress,const ScDocument & rDoc)1198 ScRefFlags parseAddress(const OUString& rString, ScAddress& rAddress, const ScDocument& rDoc)
1199 {
1200     // start with the address convention set in the document
1201     formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
1202     ScRefFlags nResult = rAddress.Parse(rString, rDoc, eConv);
1203     if ( nResult & ScRefFlags::VALID )
1204         return nResult;
1205 
1206     // try the default calc address convention
1207     nResult = rAddress.Parse(rString, rDoc);
1208     if ( nResult & ScRefFlags::VALID )
1209         return nResult;
1210 
1211     // try excel a1
1212     nResult = rAddress.Parse(rString, rDoc, formula::FormulaGrammar::CONV_XL_A1);
1213     if ( nResult & ScRefFlags::VALID )
1214         return nResult;
1215 
1216     // try r1c1
1217     return rAddress.Parse(rString, rDoc, formula::FormulaGrammar::CONV_XL_R1C1);
1218 }
1219 
transformURL(const OUString & rOldURL,OUString & rNewURL,const ScDocument & rDoc)1220 void transformURL(const OUString& rOldURL, OUString& rNewURL, const ScDocument& rDoc)
1221 {
1222     if (rOldURL.startsWith("#"))
1223     {
1224         //  URL has to be decoded for escaped characters (%20)
1225         OUString aURL = INetURLObject::decode( rOldURL,
1226                 INetURLObject::DecodeMechanism::WithCharset );
1227         OUString aAddressString = aURL.copy(1);
1228 
1229         ScRange aRange;
1230         ScRefFlags nResult = parseRange(aAddressString, aRange, rDoc);
1231         if ( nResult & ScRefFlags::VALID )
1232         {
1233             OUString aString = aRange.Format(rDoc, nResult, formula::FormulaGrammar::CONV_XL_OOX);
1234             rNewURL = "#" + aString;
1235             return;
1236         }
1237         else
1238         {
1239             ScAddress aAddress;
1240             nResult = parseAddress(aAddressString, aAddress, rDoc);
1241             if( nResult & ScRefFlags::VALID )
1242             {
1243                 OUString aString = aAddress.Format(nResult, &rDoc, formula::FormulaGrammar::CONV_XL_OOX);
1244                 rNewURL = "#" + aString;
1245                 return;
1246             }
1247         }
1248     }
1249 
1250     rNewURL = rOldURL;
1251 }
1252 
1253 }
1254 
ScURLTransformer(ScDocument & rDoc)1255 ScURLTransformer::ScURLTransformer(ScDocument& rDoc)
1256     : mrDoc(rDoc)
1257 {
1258 }
1259 
getTransformedString(const OUString & rURL) const1260 OUString ScURLTransformer::getTransformedString(const OUString& rURL) const
1261 {
1262     OUString aNewURL;
1263     transformURL(rURL, aNewURL, mrDoc);
1264     return aNewURL;
1265 }
1266 
isExternalURL(const OUString & rURL) const1267 bool ScURLTransformer::isExternalURL(const OUString& rURL) const
1268 {
1269     return !rURL.startsWith("#");
1270 }
1271 
SaveXml(XclExpXmlStream & rStrm)1272 void XclObjAny::SaveXml( XclExpXmlStream& rStrm )
1273 {
1274     // ignore group shapes at the moment, we don't process them correctly
1275     // leading to ms2010 rejecting the content
1276     if( !mxShape.is() || mxShape->getShapeType() == "com.sun.star.drawing.GroupShape" )
1277         return;
1278 
1279     // Do not output any of the detective shapes and validation circles.
1280     SdrObject* pObject = GetSdrObjectFromXShape(mxShape);
1281     if (pObject)
1282     {
1283         ScDocument& rDoc = rStrm.GetRoot().GetDoc();
1284         ScDetectiveFunc aDetFunc(rDoc, mnScTab);
1285         ScAddress       aPosition;
1286         ScRange         aSourceRange;
1287         bool            bRedLine;
1288         ScDetectiveObjType eObjType
1289             = aDetFunc.GetDetectiveObjectType(pObject, mnScTab, aPosition, aSourceRange, bRedLine);
1290 
1291         if (eObjType != SC_DETOBJ_NONE)
1292             return;
1293     }
1294 
1295     sax_fastparser::FSHelperPtr pDrawing = rStrm.GetCurrentStream();
1296 
1297     ShapeExport aDML(XML_xdr, pDrawing, nullptr, &rStrm, drawingml::DOCUMENT_XLSX);
1298     auto pURLTransformer = std::make_shared<ScURLTransformer>(*mpDoc);
1299     aDML.SetURLTranslator(pURLTransformer);
1300 
1301     pDrawing->startElement( FSNS( XML_xdr, XML_twoCellAnchor ), // OOXTODO: oneCellAnchor, absoluteAnchor
1302             XML_editAs, GetEditAs( *this ) );
1303     Reference< XPropertySet > xPropSet( mxShape, UNO_QUERY );
1304     if (xPropSet.is())
1305     {
1306         WriteFromTo( rStrm, *this );
1307         aDML.WriteShape( mxShape );
1308     }
1309 
1310     pDrawing->singleElement( FSNS( XML_xdr, XML_clientData)
1311             // OOXTODO: XML_fLocksWithSheet
1312             // OOXTODO: XML_fPrintsWithSheet
1313     );
1314     pDrawing->endElement( FSNS( XML_xdr, XML_twoCellAnchor ) );
1315 }
1316 
SaveCont(XclExpStream & rStrm)1317 void ExcBof8_Base::SaveCont( XclExpStream& rStrm )
1318 {
1319     rStrm.DisableEncryption();
1320     rStrm   << nVers << nDocType << nRupBuild << nRupYear
1321             << sal_uInt32(0)/*nFileHistory*/
1322             << sal_uInt32(0x06) /*nLowestBiffVer = Biff8*/;
1323 }
1324 
GetNum() const1325 sal_uInt16 ExcBof8_Base::GetNum() const
1326 {
1327     return 0x0809;
1328 }
1329 
GetLen() const1330 std::size_t ExcBof8_Base::GetLen() const
1331 {
1332     return 16;
1333 }
1334 
1335 // --- class ExcBof8 -------------------------------------------------
1336 
ExcBof8()1337 ExcBof8::ExcBof8()
1338 {
1339     nDocType = 0x0010;
1340 }
1341 
1342 // --- class ExcBofW8 ------------------------------------------------
1343 
ExcBofW8()1344 ExcBofW8::ExcBofW8()
1345 {
1346     nDocType = 0x0005;
1347 }
1348 
1349 // --- class ExcBundlesheet8 -----------------------------------------
1350 
ExcBundlesheet8(const RootData & rRootData,SCTAB _nTab)1351 ExcBundlesheet8::ExcBundlesheet8( const RootData& rRootData, SCTAB _nTab ) :
1352     ExcBundlesheetBase( rRootData, static_cast<sal_uInt16>(_nTab) ),
1353     sUnicodeName( rRootData.pER->GetTabInfo().GetScTabName( _nTab ) )
1354 {
1355 }
1356 
ExcBundlesheet8(const OUString & rString)1357 ExcBundlesheet8::ExcBundlesheet8( const OUString& rString ) :
1358     ExcBundlesheetBase(),
1359     sUnicodeName( rString )
1360 {
1361 }
1362 
SaveCont(XclExpStream & rStrm)1363 void ExcBundlesheet8::SaveCont( XclExpStream& rStrm )
1364 {
1365     m_nOwnPos = rStrm.GetSvStreamPos();
1366     // write dummy position, real position comes later
1367     rStrm.DisableEncryption();
1368     rStrm << sal_uInt32(0);
1369     rStrm.EnableEncryption();
1370     rStrm << nGrbit << GetName();
1371 }
1372 
GetLen() const1373 std::size_t ExcBundlesheet8::GetLen() const
1374 {   // Text max 255 chars
1375     return 8 + GetName().GetBufferSize();
1376 }
1377 
SaveXml(XclExpXmlStream & rStrm)1378 void ExcBundlesheet8::SaveXml( XclExpXmlStream& rStrm )
1379 {
1380     OUString sId;
1381     rStrm.CreateOutputStream(
1382             XclXmlUtils::GetStreamName( "xl/", "worksheets/sheet", nTab+1),
1383             XclXmlUtils::GetStreamName( nullptr, "worksheets/sheet", nTab+1),
1384             rStrm.GetCurrentStream()->getOutputStream(),
1385             "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
1386             oox::getRelationship(Relationship::WORKSHEET),
1387             &sId );
1388 
1389     rStrm.GetCurrentStream()->singleElement( XML_sheet,
1390             XML_name,               sUnicodeName.toUtf8(),
1391             XML_sheetId,            OString::number( nTab+1 ),
1392             XML_state,              nGrbit == 0x0000 ? "visible" : "hidden",
1393             FSNS( XML_r, XML_id ),  sId.toUtf8() );
1394 }
1395 
1396 // --- class XclObproj -----------------------------------------------
1397 
GetNum() const1398 sal_uInt16 XclObproj::GetNum() const
1399 {
1400     return 0x00D3;
1401 }
1402 
GetLen() const1403 std::size_t XclObproj::GetLen() const
1404 {
1405     return 0;
1406 }
1407 
1408 // ---- class XclCodename --------------------------------------------
1409 
XclCodename(const OUString & r)1410 XclCodename::XclCodename( const OUString& r ) : aName( r )
1411 {
1412 }
1413 
SaveCont(XclExpStream & rStrm)1414 void XclCodename::SaveCont( XclExpStream& rStrm )
1415 {
1416     rStrm << aName;
1417 }
1418 
GetNum() const1419 sal_uInt16 XclCodename::GetNum() const
1420 {
1421     return 0x01BA;
1422 }
1423 
GetLen() const1424 std::size_t XclCodename::GetLen() const
1425 {
1426     return aName.GetSize();
1427 }
1428 
1429 // ---- Scenarios ----------------------------------------------------
1430 
ExcEScenarioCell(sal_uInt16 nC,sal_uInt16 nR,const OUString & rTxt)1431 ExcEScenarioCell::ExcEScenarioCell( sal_uInt16 nC, sal_uInt16 nR, const OUString& rTxt ) :
1432         nCol( nC ),
1433         nRow( nR ),
1434         sText( rTxt, XclStrFlags::NONE, 255 )
1435 {
1436 }
1437 
WriteAddress(XclExpStream & rStrm) const1438 void ExcEScenarioCell::WriteAddress( XclExpStream& rStrm ) const
1439 {
1440     rStrm << nRow << nCol;
1441 }
1442 
WriteText(XclExpStream & rStrm) const1443 void ExcEScenarioCell::WriteText( XclExpStream& rStrm ) const
1444 {
1445     rStrm << sText;
1446 }
1447 
SaveXml(XclExpXmlStream & rStrm) const1448 void ExcEScenarioCell::SaveXml( XclExpXmlStream& rStrm ) const
1449 {
1450     rStrm.GetCurrentStream()->singleElement( XML_inputCells,
1451             // OOXTODO: XML_deleted,
1452             // OOXTODO: XML_numFmtId,
1453             XML_r,      XclXmlUtils::ToOString( rStrm.GetRoot().GetDoc(), ScAddress( nCol, nRow, 0 ) ),
1454             // OOXTODO: XML_undone,
1455             XML_val,    XclXmlUtils::ToOString( sText ) );
1456 }
1457 
ExcEScenario(const XclExpRoot & rRoot,SCTAB nTab)1458 ExcEScenario::ExcEScenario( const XclExpRoot& rRoot, SCTAB nTab )
1459 {
1460     OUString  sTmpName;
1461     OUString  sTmpComm;
1462     OUString aTmp;
1463     Color   aDummyCol;
1464     ScScenarioFlags nFlags;
1465 
1466     ScDocument& rDoc = rRoot.GetDoc();
1467     rDoc.GetName(nTab, aTmp);
1468     sTmpName = aTmp;
1469     sName.Assign( sTmpName, XclStrFlags::EightBitLength );
1470     nRecLen = 8 + sName.GetBufferSize();
1471 
1472     rDoc.GetScenarioData( nTab, aTmp, aDummyCol, nFlags );
1473     sTmpComm = aTmp;
1474     sComment.Assign( sTmpComm, XclStrFlags::NONE, 255 );
1475     if( sComment.Len() )
1476         nRecLen += sComment.GetSize();
1477     bProtected = (nFlags & ScScenarioFlags::Protected) != ScScenarioFlags::NONE;
1478 
1479     sUserName.Assign( rRoot.GetUserName(), XclStrFlags::NONE, 255 );
1480     nRecLen += sUserName.GetSize();
1481 
1482     const ScRangeList* pRList = rDoc.GetScenarioRanges( nTab );
1483     if( !pRList )
1484         return;
1485 
1486     bool    bContLoop = true;
1487     SCROW   nRow;
1488     SCCOL   nCol;
1489     OUString  sText;
1490     double  fVal;
1491 
1492     for( size_t nRange = 0; (nRange < pRList->size()) && bContLoop; nRange++ )
1493     {
1494         const ScRange & rRange = (*pRList)[nRange];
1495         for( nRow = rRange.aStart.Row(); (nRow <= rRange.aEnd.Row()) && bContLoop; nRow++ )
1496             for( nCol = rRange.aStart.Col(); (nCol <= rRange.aEnd.Col()) && bContLoop; nCol++ )
1497             {
1498                 if( rDoc.HasValueData( nCol, nRow, nTab ) )
1499                 {
1500                     rDoc.GetValue( nCol, nRow, nTab, fVal );
1501                     sText = ::rtl::math::doubleToUString( fVal,
1502                             rtl_math_StringFormat_Automatic,
1503                             rtl_math_DecimalPlaces_Max,
1504                             ScGlobal::getLocaleDataPtr()->getNumDecimalSep()[0],
1505                             true );
1506                 }
1507                 else
1508                     sText = rDoc.GetString(nCol, nRow, nTab);
1509                 bContLoop = Append( static_cast<sal_uInt16>(nCol),
1510                         static_cast<sal_uInt16>(nRow), sText );
1511             }
1512     }
1513 }
1514 
Append(sal_uInt16 nCol,sal_uInt16 nRow,const OUString & rTxt)1515 bool ExcEScenario::Append( sal_uInt16 nCol, sal_uInt16 nRow, const OUString& rTxt )
1516 {
1517     if( aCells.size() == EXC_SCEN_MAXCELL )
1518         return false;
1519 
1520     ExcEScenarioCell aCell(nCol, nRow, rTxt);
1521     aCells.push_back(aCell);
1522     nRecLen += 6 + aCell.GetStringBytes();        // 4 bytes address, 2 bytes ifmt
1523     return true;
1524 }
1525 
SaveCont(XclExpStream & rStrm)1526 void ExcEScenario::SaveCont( XclExpStream& rStrm )
1527 {
1528     sal_uInt16 count = aCells.size();
1529 
1530     rStrm   << count                            // number of cells
1531             << sal_uInt8(bProtected)            // fProtection
1532             << sal_uInt8(0)                    // fHidden
1533             << static_cast<sal_uInt8>(sName.Len())          // length of scen name
1534             << static_cast<sal_uInt8>(sComment.Len())       // length of comment
1535             << static_cast<sal_uInt8>(sUserName.Len());     // length of user name
1536     sName.WriteFlagField( rStrm );
1537     sName.WriteBuffer( rStrm );
1538 
1539     rStrm << sUserName;
1540 
1541     if( sComment.Len() )
1542         rStrm << sComment;
1543 
1544     for( const auto& rCell : aCells )
1545         rCell.WriteAddress( rStrm );           // pos of cell
1546     for( const auto& rCell : aCells )
1547         rCell.WriteText( rStrm );              // string content
1548     rStrm.SetSliceSize( 2 );
1549     rStrm.WriteZeroBytes( 2 * count );  // date format
1550 }
1551 
GetNum() const1552 sal_uInt16 ExcEScenario::GetNum() const
1553 {
1554     return 0x00AF;
1555 }
1556 
GetLen() const1557 std::size_t ExcEScenario::GetLen() const
1558 {
1559     return nRecLen;
1560 }
1561 
SaveXml(XclExpXmlStream & rStrm)1562 void ExcEScenario::SaveXml( XclExpXmlStream& rStrm )
1563 {
1564     sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
1565     rWorkbook->startElement( XML_scenario,
1566             XML_name,       XclXmlUtils::ToOString( sName ).getStr(),
1567             XML_locked,     ToPsz( bProtected ),
1568             // OOXTODO: XML_hidden,
1569             XML_count,      OString::number(  aCells.size() ).getStr(),
1570             XML_user,       XESTRING_TO_PSZ( sUserName ),
1571             XML_comment,    XESTRING_TO_PSZ( sComment ) );
1572 
1573     for( const auto& rCell : aCells )
1574         rCell.SaveXml( rStrm );
1575 
1576     rWorkbook->endElement( XML_scenario );
1577 }
1578 
ExcEScenarioManager(const XclExpRoot & rRoot,SCTAB nTab)1579 ExcEScenarioManager::ExcEScenarioManager( const XclExpRoot& rRoot, SCTAB nTab ) :
1580         nActive( 0 )
1581 {
1582     ScDocument& rDoc = rRoot.GetDoc();
1583     if( rDoc.IsScenario( nTab ) )
1584         return;
1585 
1586     SCTAB nFirstTab = nTab + 1;
1587     SCTAB nNewTab       = nFirstTab;
1588 
1589     while( rDoc.IsScenario( nNewTab ) )
1590     {
1591         aScenes.emplace_back( rRoot, nNewTab  );
1592 
1593         if( rDoc.IsActiveScenario( nNewTab ) )
1594             nActive = static_cast<sal_uInt16>(nNewTab - nFirstTab);
1595         nNewTab++;
1596     }
1597 }
1598 
~ExcEScenarioManager()1599 ExcEScenarioManager::~ExcEScenarioManager()
1600 {
1601 }
1602 
SaveCont(XclExpStream & rStrm)1603 void ExcEScenarioManager::SaveCont( XclExpStream& rStrm )
1604 {
1605     rStrm   << static_cast<sal_uInt16>(aScenes.size())  // number of scenarios
1606             << nActive                      // active scen
1607             << nActive                      // last displayed
1608             << sal_uInt16(0);              // reference areas
1609 }
1610 
Save(XclExpStream & rStrm)1611 void ExcEScenarioManager::Save( XclExpStream& rStrm )
1612 {
1613     if( !aScenes.empty() )
1614         ExcRecord::Save( rStrm );
1615 
1616     for( ExcEScenario& rScenario : aScenes )
1617         rScenario.Save( rStrm );
1618 }
1619 
SaveXml(XclExpXmlStream & rStrm)1620 void ExcEScenarioManager::SaveXml( XclExpXmlStream& rStrm )
1621 {
1622     if( aScenes.empty() )
1623         return;
1624 
1625     sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
1626     rWorkbook->startElement( XML_scenarios,
1627             XML_current,    OString::number( nActive ),
1628             XML_show,       OString::number( nActive )
1629             // OOXTODO: XML_sqref
1630     );
1631 
1632     for( ExcEScenario& rScenario : aScenes )
1633         rScenario.SaveXml( rStrm );
1634 
1635     rWorkbook->endElement( XML_scenarios );
1636 }
1637 
GetNum() const1638 sal_uInt16 ExcEScenarioManager::GetNum() const
1639 {
1640     return 0x00AE;
1641 }
1642 
GetLen() const1643 std::size_t ExcEScenarioManager::GetLen() const
1644 {
1645     return 8;
1646 }
1647 
1648 namespace {
1649 
1650 struct XclExpTabProtectOption
1651 {
1652     ScTableProtection::Option   eOption;
1653     sal_uInt16                  nMask;
1654 };
1655 
1656 }
1657 
XclExpSheetProtectOptions(const XclExpRoot & rRoot,SCTAB nTab)1658 XclExpSheetProtectOptions::XclExpSheetProtectOptions( const XclExpRoot& rRoot, SCTAB nTab ) :
1659     XclExpRecord( 0x0867, 23 )
1660 {
1661     static const XclExpTabProtectOption aTable[] =
1662     {
1663         { ScTableProtection::OBJECTS,               0x0001 },
1664         { ScTableProtection::SCENARIOS,             0x0002 },
1665         { ScTableProtection::FORMAT_CELLS,          0x0004 },
1666         { ScTableProtection::FORMAT_COLUMNS,        0x0008 },
1667         { ScTableProtection::FORMAT_ROWS,           0x0010 },
1668         { ScTableProtection::INSERT_COLUMNS,        0x0020 },
1669         { ScTableProtection::INSERT_ROWS,           0x0040 },
1670         { ScTableProtection::INSERT_HYPERLINKS,     0x0080 },
1671 
1672         { ScTableProtection::DELETE_COLUMNS,        0x0100 },
1673         { ScTableProtection::DELETE_ROWS,           0x0200 },
1674         { ScTableProtection::SELECT_LOCKED_CELLS,   0x0400 },
1675         { ScTableProtection::SORT,                  0x0800 },
1676         { ScTableProtection::AUTOFILTER,            0x1000 },
1677         { ScTableProtection::PIVOT_TABLES,          0x2000 },
1678         { ScTableProtection::SELECT_UNLOCKED_CELLS, 0x4000 },
1679 
1680         { ScTableProtection::NONE,                  0x0000 }
1681     };
1682 
1683     mnOptions = 0x0000;
1684     const ScTableProtection* pProtect = rRoot.GetDoc().GetTabProtection(nTab);
1685     if (!pProtect)
1686         return;
1687 
1688     for (int i = 0; aTable[i].nMask != 0x0000; ++i)
1689     {
1690         if ( pProtect->isOptionEnabled(aTable[i].eOption) )
1691             mnOptions |= aTable[i].nMask;
1692     }
1693 }
1694 
WriteBody(XclExpStream & rStrm)1695 void XclExpSheetProtectOptions::WriteBody( XclExpStream& rStrm )
1696 {
1697     sal_uInt16 nBytes = 0x0867;
1698     rStrm << nBytes;
1699 
1700     for (int i = 0; i < 9; ++i)
1701         rStrm << static_cast<unsigned char>(0);
1702 
1703     nBytes = 0x0200;
1704     rStrm << nBytes;
1705     nBytes = 0x0100;
1706     rStrm << nBytes;
1707     nBytes = 0xFFFF;
1708     rStrm << nBytes << nBytes;
1709 
1710     rStrm << mnOptions;
1711     nBytes = 0;
1712     rStrm << nBytes;
1713 }
1714 
XclExpSheetEnhancedProtection(const XclExpRoot & rRoot,const ScEnhancedProtection & rProt)1715 XclExpSheetEnhancedProtection::XclExpSheetEnhancedProtection( const XclExpRoot& rRoot,
1716         const ScEnhancedProtection & rProt ) :
1717     XclExpRecord( 0x0868 ),
1718     mrRoot( rRoot ),
1719     maEnhancedProtection( rProt )
1720 {
1721 }
1722 
WriteBody(XclExpStream & rStrm)1723 void XclExpSheetEnhancedProtection::WriteBody( XclExpStream& rStrm )
1724 {
1725     sal_uInt16 const nRecordType = 0x0868;
1726     rStrm << nRecordType;                   // frtHeader rt
1727     rStrm.WriteZeroBytesToRecord(10);       // frtHeader unused
1728     rStrm << EXC_ISFPROTECTION;             // isf
1729     rStrm.WriteZeroBytesToRecord(5);        // reserved1 (1 bytes) and reserved2 (4 bytes)
1730 
1731     XclRangeList aRefs;
1732     if (maEnhancedProtection.maRangeList.is())
1733         mrRoot.GetAddressConverter().ConvertRangeList( aRefs, *maEnhancedProtection.maRangeList, false);
1734     sal_uInt16 nCref = ulimit_cast<sal_uInt16>(aRefs.size());
1735     rStrm << nCref;                         // cref
1736     rStrm.WriteZeroBytesToRecord(6);        // cbFeatData if EXC_ISFFEC2 (4 bytes) and reserved3 (2 bytes)
1737     aRefs.Write( rStrm, true, nCref);       // refs
1738 
1739     // FeatProtection structure
1740     rStrm << maEnhancedProtection.mnAreserved;              // 1 bit A and 31 bits reserved
1741     rStrm << maEnhancedProtection.mnPasswordVerifier;       // wPassword
1742     rStrm << XclExpString( maEnhancedProtection.maTitle);   // stTitle
1743     bool bSDContainer = ((maEnhancedProtection.mnAreserved & 0x00000001) == 0x00000001);
1744     sal_uInt32 nCbSD = maEnhancedProtection.maSecurityDescriptor.size();
1745     SAL_WARN_IF( bSDContainer && nCbSD < 20, "sc.filter",
1746             "XclExpSheetEnhancedProtection A flag indicates container but cbSD < 20");
1747     SAL_WARN_IF( !bSDContainer && nCbSD > 0, "sc.filter",
1748             "XclExpSheetEnhancedProtection A flag indicates no container but cbSD > 0");
1749     if (bSDContainer)
1750     {
1751         rStrm << nCbSD;
1752         rStrm.Write( &maEnhancedProtection.maSecurityDescriptor.front(), nCbSD);
1753     }
1754 }
1755 
SaveCont(XclExpStream & rStrm)1756 void XclCalccount::SaveCont( XclExpStream& rStrm )
1757 {
1758     rStrm << nCount;
1759 }
1760 
XclCalccount(const ScDocument & rDoc)1761 XclCalccount::XclCalccount( const ScDocument& rDoc )
1762 {
1763     nCount = rDoc.GetDocOptions().GetIterCount();
1764 }
1765 
GetNum() const1766 sal_uInt16 XclCalccount::GetNum() const
1767 {
1768     return 0x000C;
1769 }
1770 
GetLen() const1771 std::size_t XclCalccount::GetLen() const
1772 {
1773     return 2;
1774 }
1775 
SaveXml(XclExpXmlStream & rStrm)1776 void XclCalccount::SaveXml( XclExpXmlStream& rStrm )
1777 {
1778     rStrm.WriteAttributes(XML_iterateCount, OUString::number(nCount));
1779 }
1780 
SaveCont(XclExpStream & rStrm)1781 void XclIteration::SaveCont( XclExpStream& rStrm )
1782 {
1783     rStrm << nIter;
1784 }
1785 
XclIteration(const ScDocument & rDoc)1786 XclIteration::XclIteration( const ScDocument& rDoc )
1787 {
1788     nIter = rDoc.GetDocOptions().IsIter()? 1 : 0;
1789 }
1790 
GetNum() const1791 sal_uInt16 XclIteration::GetNum() const
1792 {
1793     return 0x0011;
1794 }
1795 
GetLen() const1796 std::size_t XclIteration::GetLen() const
1797 {
1798     return 2;
1799 }
1800 
SaveXml(XclExpXmlStream & rStrm)1801 void XclIteration::SaveXml( XclExpXmlStream& rStrm )
1802 {
1803     rStrm.WriteAttributes(XML_iterate, ToPsz(nIter == 1));
1804 }
1805 
SaveCont(XclExpStream & rStrm)1806 void XclDelta::SaveCont( XclExpStream& rStrm )
1807 {
1808     rStrm << fDelta;
1809 }
1810 
XclDelta(const ScDocument & rDoc)1811 XclDelta::XclDelta( const ScDocument& rDoc )
1812 {
1813     fDelta = rDoc.GetDocOptions().GetIterEps();
1814 }
1815 
GetNum() const1816 sal_uInt16 XclDelta::GetNum() const
1817 {
1818     return 0x0010;
1819 }
1820 
GetLen() const1821 std::size_t XclDelta::GetLen() const
1822 {
1823     return 8;
1824 }
1825 
SaveXml(XclExpXmlStream & rStrm)1826 void XclDelta::SaveXml( XclExpXmlStream& rStrm )
1827 {
1828     rStrm.WriteAttributes(XML_iterateDelta, OUString::number(fDelta));
1829 }
1830 
XclExpFileEncryption(const XclExpRoot & rRoot)1831 XclExpFileEncryption::XclExpFileEncryption( const XclExpRoot& rRoot ) :
1832     XclExpRecord(0x002F, 54),
1833     mrRoot(rRoot)
1834 {
1835 }
1836 
~XclExpFileEncryption()1837 XclExpFileEncryption::~XclExpFileEncryption()
1838 {
1839 }
1840 
WriteBody(XclExpStream & rStrm)1841 void XclExpFileEncryption::WriteBody( XclExpStream& rStrm )
1842 {
1843     // 0x0000 - neither standard nor strong encryption
1844     // 0x0001 - standard or strong encryption
1845     rStrm << static_cast<sal_uInt16>(0x0001);
1846 
1847     // 0x0000 - non standard encryption
1848     // 0x0001 - standard encryption
1849     sal_uInt16 nStdEnc = 0x0001;
1850     rStrm << nStdEnc << nStdEnc;
1851 
1852     sal_uInt8 pnDocId[16];
1853     sal_uInt8 pnSalt[16];
1854     sal_uInt8 pnSaltHash[16];
1855     XclExpEncrypterRef xEnc = std::make_shared<XclExpBiff8Encrypter>(mrRoot);
1856     xEnc->GetDocId(pnDocId);
1857     xEnc->GetSalt(pnSalt);
1858     xEnc->GetSaltDigest(pnSaltHash);
1859 
1860     rStrm.Write(pnDocId, 16);
1861     rStrm.Write(pnSalt, 16);
1862     rStrm.Write(pnSaltHash, 16);
1863 
1864     rStrm.SetEncrypter(xEnc);
1865 }
1866 
XclExpInterfaceHdr(sal_uInt16 nCodePage)1867 XclExpInterfaceHdr::XclExpInterfaceHdr( sal_uInt16 nCodePage ) :
1868     XclExpUInt16Record( EXC_ID_INTERFACEHDR, nCodePage )
1869 {
1870 }
1871 
WriteBody(XclExpStream & rStrm)1872 void XclExpInterfaceHdr::WriteBody( XclExpStream& rStrm )
1873 {
1874     rStrm.DisableEncryption();
1875     rStrm << GetValue();
1876 }
1877 
XclExpInterfaceEnd()1878 XclExpInterfaceEnd::XclExpInterfaceEnd() :
1879     XclExpRecord(0x00E2, 0) {}
1880 
~XclExpInterfaceEnd()1881 XclExpInterfaceEnd::~XclExpInterfaceEnd() {}
1882 
WriteBody(XclExpStream & rStrm)1883 void XclExpInterfaceEnd::WriteBody( XclExpStream& rStrm )
1884 {
1885     // Don't forget to re-enable encryption.
1886     rStrm.EnableEncryption();
1887 }
1888 
XclExpWriteAccess()1889 XclExpWriteAccess::XclExpWriteAccess() :
1890     XclExpRecord(0x005C, 112)
1891 {
1892 }
1893 
~XclExpWriteAccess()1894 XclExpWriteAccess::~XclExpWriteAccess()
1895 {
1896 }
1897 
WriteBody(XclExpStream & rStrm)1898 void XclExpWriteAccess::WriteBody( XclExpStream& rStrm )
1899 {
1900     static const sal_uInt8 aData[] = {
1901         0x04, 0x00, 0x00,  'C',  'a',  'l',  'c', 0x20,
1902         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1903         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1904         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1905         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1906         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1907         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1908         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1909         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1910         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1911         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1912         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1913         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
1914         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
1915 
1916     for (std::size_t i = 0; i < sizeof(aData); ++i)
1917         rStrm << aData[i];
1918 }
1919 
XclExpFileSharing(const XclExpRoot & rRoot,sal_uInt16 nPasswordHash,bool bRecommendReadOnly)1920 XclExpFileSharing::XclExpFileSharing( const XclExpRoot& rRoot, sal_uInt16 nPasswordHash, bool bRecommendReadOnly ) :
1921     XclExpRecord( EXC_ID_FILESHARING ),
1922     mnPasswordHash( nPasswordHash ),
1923     mbRecommendReadOnly( bRecommendReadOnly )
1924 {
1925     if( rRoot.GetBiff() <= EXC_BIFF5 )
1926         maUserName.AssignByte( rRoot.GetUserName(), rRoot.GetTextEncoding(), XclStrFlags::EightBitLength );
1927     else
1928         maUserName.Assign( rRoot.GetUserName() );
1929 }
1930 
Save(XclExpStream & rStrm)1931 void XclExpFileSharing::Save( XclExpStream& rStrm )
1932 {
1933     if( (mnPasswordHash != 0) || mbRecommendReadOnly )
1934         XclExpRecord::Save( rStrm );
1935 }
1936 
WriteBody(XclExpStream & rStrm)1937 void XclExpFileSharing::WriteBody( XclExpStream& rStrm )
1938 {
1939     rStrm << sal_uInt16( mbRecommendReadOnly ? 1 : 0 ) << mnPasswordHash << maUserName;
1940 }
1941 
XclExpProt4Rev()1942 XclExpProt4Rev::XclExpProt4Rev() :
1943     XclExpRecord(0x01AF, 2)
1944 {
1945 }
1946 
~XclExpProt4Rev()1947 XclExpProt4Rev::~XclExpProt4Rev()
1948 {
1949 }
1950 
WriteBody(XclExpStream & rStrm)1951 void XclExpProt4Rev::WriteBody( XclExpStream& rStrm )
1952 {
1953     rStrm << static_cast<sal_uInt16>(0x0000);
1954 }
1955 
XclExpProt4RevPass()1956 XclExpProt4RevPass::XclExpProt4RevPass() :
1957     XclExpRecord(0x01BC, 2)
1958 {
1959 }
1960 
~XclExpProt4RevPass()1961 XclExpProt4RevPass::~XclExpProt4RevPass()
1962 {
1963 }
1964 
WriteBody(XclExpStream & rStrm)1965 void XclExpProt4RevPass::WriteBody( XclExpStream& rStrm )
1966 {
1967     rStrm << static_cast<sal_uInt16>(0x0000);
1968 }
1969 
1970 const sal_uInt8 nDataRecalcId[] = {
1971     0xC1, 0x01, 0x00, 0x00, 0x54, 0x8D, 0x01, 0x00
1972 };
1973 
XclExpRecalcId()1974 XclExpRecalcId::XclExpRecalcId() :
1975     XclExpDummyRecord(0x01C1, nDataRecalcId, sizeof(nDataRecalcId))
1976 {
1977 }
1978 
1979 const sal_uInt8 nDataBookExt[] = {
1980     0x63, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1981     0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1982     0x02
1983 };
1984 
XclExpBookExt()1985 XclExpBookExt::XclExpBookExt() :
1986     XclExpDummyRecord(0x0863, nDataBookExt, sizeof(nDataBookExt))
1987 {
1988 }
1989 
XclRefmode(const ScDocument & rDoc)1990 XclRefmode::XclRefmode( const ScDocument& rDoc ) :
1991     XclExpBoolRecord( 0x000F, rDoc.GetAddressConvention() != formula::FormulaGrammar::CONV_XL_R1C1 )
1992 {
1993 }
1994 
SaveXml(XclExpXmlStream & rStrm)1995 void XclRefmode::SaveXml( XclExpXmlStream& rStrm )
1996 {
1997     rStrm.WriteAttributes(XML_refMode, GetBool() ? "A1" : "R1C1");
1998 }
1999 
2000 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2001