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