1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 #include "oox/xls/drawingmanager.hxx" 25 26 #include <com/sun/star/awt/Rectangle.hpp> 27 #include <com/sun/star/drawing/CircleKind.hpp> 28 #include <com/sun/star/drawing/PointSequenceSequence.hpp> 29 #include <com/sun/star/drawing/PolygonKind.hpp> 30 #include <com/sun/star/drawing/XShapes.hpp> 31 #include "oox/core/filterbase.hxx" 32 #include "oox/drawingml/fillproperties.hxx" 33 #include "oox/drawingml/lineproperties.hxx" 34 #include "oox/drawingml/shapepropertymap.hxx" 35 #include "oox/helper/containerhelper.hxx" 36 #include "oox/token/tokens.hxx" 37 #include "oox/xls/biffinputstream.hxx" 38 #include "oox/xls/unitconverter.hxx" 39 40 namespace oox { 41 namespace xls { 42 43 // ============================================================================ 44 45 using namespace ::com::sun::star::awt; 46 using namespace ::com::sun::star::drawing; 47 using namespace ::com::sun::star::lang; 48 using namespace ::com::sun::star::uno; 49 using namespace ::oox::drawingml; 50 51 using ::rtl::OUString; 52 53 // ============================================================================ 54 55 namespace { 56 57 // OBJ record ----------------------------------------------------------------- 58 59 const sal_uInt16 BIFF_OBJTYPE_GROUP = 0; 60 const sal_uInt16 BIFF_OBJTYPE_LINE = 1; 61 const sal_uInt16 BIFF_OBJTYPE_RECTANGLE = 2; 62 const sal_uInt16 BIFF_OBJTYPE_OVAL = 3; 63 const sal_uInt16 BIFF_OBJTYPE_ARC = 4; 64 const sal_uInt16 BIFF_OBJTYPE_CHART = 5; 65 const sal_uInt16 BIFF_OBJTYPE_TEXT = 6; 66 const sal_uInt16 BIFF_OBJTYPE_BUTTON = 7; 67 const sal_uInt16 BIFF_OBJTYPE_PICTURE = 8; 68 const sal_uInt16 BIFF_OBJTYPE_POLYGON = 9; // new in BIFF4 69 const sal_uInt16 BIFF_OBJTYPE_CHECKBOX = 11; // new in BIFF5 70 const sal_uInt16 BIFF_OBJTYPE_OPTIONBUTTON = 12; 71 const sal_uInt16 BIFF_OBJTYPE_EDIT = 13; 72 const sal_uInt16 BIFF_OBJTYPE_LABEL = 14; 73 const sal_uInt16 BIFF_OBJTYPE_DIALOG = 15; 74 const sal_uInt16 BIFF_OBJTYPE_SPIN = 16; 75 const sal_uInt16 BIFF_OBJTYPE_SCROLLBAR = 17; 76 const sal_uInt16 BIFF_OBJTYPE_LISTBOX = 18; 77 const sal_uInt16 BIFF_OBJTYPE_GROUPBOX = 19; 78 const sal_uInt16 BIFF_OBJTYPE_DROPDOWN = 20; 79 const sal_uInt16 BIFF_OBJTYPE_NOTE = 25; // new in BIFF8 80 const sal_uInt16 BIFF_OBJTYPE_DRAWING = 30; 81 const sal_uInt16 BIFF_OBJTYPE_UNKNOWN = 0xFFFF; // for internal use only 82 83 const sal_uInt16 BIFF_OBJ_HIDDEN = 0x0100; 84 const sal_uInt16 BIFF_OBJ_VISIBLE = 0x0200; 85 const sal_uInt16 BIFF_OBJ_PRINTABLE = 0x0400; 86 87 // line formatting ------------------------------------------------------------ 88 89 const sal_uInt8 BIFF_OBJ_LINE_AUTOCOLOR = 64; 90 91 const sal_uInt8 BIFF_OBJ_LINE_SOLID = 0; 92 const sal_uInt8 BIFF_OBJ_LINE_DASH = 1; 93 const sal_uInt8 BIFF_OBJ_LINE_DOT = 2; 94 const sal_uInt8 BIFF_OBJ_LINE_DASHDOT = 3; 95 const sal_uInt8 BIFF_OBJ_LINE_DASHDOTDOT = 4; 96 const sal_uInt8 BIFF_OBJ_LINE_MEDTRANS = 5; 97 const sal_uInt8 BIFF_OBJ_LINE_DARKTRANS = 6; 98 const sal_uInt8 BIFF_OBJ_LINE_LIGHTTRANS = 7; 99 const sal_uInt8 BIFF_OBJ_LINE_NONE = 255; 100 101 const sal_uInt8 BIFF_OBJ_LINE_HAIR = 0; 102 const sal_uInt8 BIFF_OBJ_LINE_THIN = 1; 103 const sal_uInt8 BIFF_OBJ_LINE_MEDIUM = 2; 104 const sal_uInt8 BIFF_OBJ_LINE_THICK = 3; 105 106 const sal_uInt8 BIFF_OBJ_LINE_AUTO = 0x01; 107 108 const sal_uInt8 BIFF_OBJ_ARROW_NONE = 0; 109 const sal_uInt8 BIFF_OBJ_ARROW_OPEN = 1; 110 const sal_uInt8 BIFF_OBJ_ARROW_FILLED = 2; 111 const sal_uInt8 BIFF_OBJ_ARROW_OPENBOTH = 3; 112 const sal_uInt8 BIFF_OBJ_ARROW_FILLEDBOTH = 4; 113 114 const sal_uInt8 BIFF_OBJ_ARROW_NARROW = 0; 115 const sal_uInt8 BIFF_OBJ_ARROW_MEDIUM = 1; 116 const sal_uInt8 BIFF_OBJ_ARROW_WIDE = 2; 117 118 const sal_uInt8 BIFF_OBJ_LINE_TL = 0; 119 const sal_uInt8 BIFF_OBJ_LINE_TR = 1; 120 const sal_uInt8 BIFF_OBJ_LINE_BR = 2; 121 const sal_uInt8 BIFF_OBJ_LINE_BL = 3; 122 123 const sal_uInt8 BIFF_OBJ_ARC_TR = 0; 124 const sal_uInt8 BIFF_OBJ_ARC_TL = 1; 125 const sal_uInt8 BIFF_OBJ_ARC_BL = 2; 126 const sal_uInt8 BIFF_OBJ_ARC_BR = 3; 127 128 const sal_uInt16 BIFF_OBJ_POLY_CLOSED = 0x0100; 129 130 // fill formatting ------------------------------------------------------------ 131 132 const sal_uInt8 BIFF_OBJ_FILL_AUTOCOLOR = 65; 133 134 const sal_uInt8 BIFF_OBJ_PATT_NONE = 0; 135 const sal_uInt8 BIFF_OBJ_PATT_SOLID = 1; 136 137 const sal_uInt8 BIFF_OBJ_FILL_AUTO = 0x01; 138 139 // text formatting ------------------------------------------------------------ 140 141 const sal_uInt8 BIFF_OBJ_HOR_LEFT = 1; 142 const sal_uInt8 BIFF_OBJ_HOR_CENTER = 2; 143 const sal_uInt8 BIFF_OBJ_HOR_RIGHT = 3; 144 const sal_uInt8 BIFF_OBJ_HOR_JUSTIFY = 4; 145 146 const sal_uInt8 BIFF_OBJ_VER_TOP = 1; 147 const sal_uInt8 BIFF_OBJ_VER_CENTER = 2; 148 const sal_uInt8 BIFF_OBJ_VER_BOTTOM = 3; 149 const sal_uInt8 BIFF_OBJ_VER_JUSTIFY = 4; 150 151 const sal_uInt16 BIFF_OBJ_ORIENT_NONE = 0; 152 const sal_uInt16 BIFF_OBJ_ORIENT_STACKED = 1; /// Stacked top to bottom. 153 const sal_uInt16 BIFF_OBJ_ORIENT_90CCW = 2; /// 90 degr. counterclockwise. 154 const sal_uInt16 BIFF_OBJ_ORIENT_90CW = 3; /// 90 degr. clockwise. 155 156 const sal_uInt16 BIFF_OBJ_TEXT_AUTOSIZE = 0x0080; 157 const sal_uInt16 BIFF_OBJ_TEXT_LOCKED = 0x0200; 158 159 const sal_Int32 BIFF_OBJ_TEXT_MARGIN = 20000; /// Automatic text margin (EMUs). 160 161 // BIFF8 OBJ sub records ------------------------------------------------------ 162 163 const sal_uInt16 BIFF_OBJCMO_PRINTABLE = 0x0010; /// Object printable. 164 const sal_uInt16 BIFF_OBJCMO_AUTOLINE = 0x2000; /// Automatic line formatting. 165 const sal_uInt16 BIFF_OBJCMO_AUTOFILL = 0x4000; /// Automatic fill formatting. 166 167 // ---------------------------------------------------------------------------- 168 169 inline BiffInputStream& operator>>( BiffInputStream& rStrm, ShapeAnchor& rAnchor ) 170 { 171 rAnchor.importBiffAnchor( rStrm ); 172 return rStrm; 173 } 174 175 } // namespace 176 177 // ============================================================================ 178 // Model structures for BIFF OBJ record data 179 // ============================================================================ 180 181 BiffObjLineModel::BiffObjLineModel() : 182 mnColorIdx( BIFF_OBJ_LINE_AUTOCOLOR ), 183 mnStyle( BIFF_OBJ_LINE_SOLID ), 184 mnWidth( BIFF_OBJ_LINE_HAIR ), 185 mbAuto( true ) 186 { 187 } 188 189 bool BiffObjLineModel::isVisible() const 190 { 191 return mbAuto || (mnStyle != BIFF_OBJ_LINE_NONE); 192 } 193 194 BiffInputStream& operator>>( BiffInputStream& rStrm, BiffObjLineModel& rModel ) 195 { 196 sal_uInt8 nFlags; 197 rStrm >> rModel.mnColorIdx >> rModel.mnStyle >> rModel.mnWidth >> nFlags; 198 rModel.mbAuto = getFlag( nFlags, BIFF_OBJ_LINE_AUTO ); 199 return rStrm; 200 } 201 202 // ============================================================================ 203 204 BiffObjFillModel::BiffObjFillModel() : 205 mnBackColorIdx( BIFF_OBJ_LINE_AUTOCOLOR ), 206 mnPattColorIdx( BIFF_OBJ_FILL_AUTOCOLOR ), 207 mnPattern( BIFF_OBJ_PATT_SOLID ), 208 mbAuto( true ) 209 { 210 } 211 212 bool BiffObjFillModel::isFilled() const 213 { 214 return mbAuto || (mnPattern != BIFF_OBJ_PATT_NONE); 215 } 216 217 BiffInputStream& operator>>( BiffInputStream& rStrm, BiffObjFillModel& rModel ) 218 { 219 sal_uInt8 nFlags; 220 rStrm >> rModel.mnBackColorIdx >> rModel.mnPattColorIdx >> rModel.mnPattern >> nFlags; 221 rModel.mbAuto = getFlag( nFlags, BIFF_OBJ_FILL_AUTO ); 222 return rStrm; 223 } 224 225 // ============================================================================ 226 227 BiffObjTextModel::BiffObjTextModel() : 228 mnTextLen( 0 ), 229 mnFormatSize( 0 ), 230 mnLinkSize( 0 ), 231 mnDefFontId( 0 ), 232 mnFlags( 0 ), 233 mnOrientation( BIFF_OBJ_ORIENT_NONE ), 234 mnButtonFlags( 0 ), 235 mnShortcut( 0 ), 236 mnShortcutEA( 0 ) 237 { 238 } 239 240 void BiffObjTextModel::readObj3( BiffInputStream& rStrm ) 241 { 242 rStrm >> mnTextLen; 243 rStrm.skip( 2 ); 244 rStrm >> mnFormatSize >> mnDefFontId; 245 rStrm.skip( 2 ); 246 rStrm >> mnFlags >> mnOrientation; 247 rStrm.skip( 8 ); 248 } 249 250 void BiffObjTextModel::readObj5( BiffInputStream& rStrm ) 251 { 252 rStrm >> mnTextLen; 253 rStrm.skip( 2 ); 254 rStrm >> mnFormatSize >> mnDefFontId; 255 rStrm.skip( 2 ); 256 rStrm >> mnFlags >> mnOrientation; 257 rStrm.skip( 2 ); 258 rStrm >> mnLinkSize; 259 rStrm.skip( 2 ); 260 rStrm >> mnButtonFlags >> mnShortcut >> mnShortcutEA; 261 } 262 263 void BiffObjTextModel::readTxo8( BiffInputStream& rStrm ) 264 { 265 rStrm >> mnFlags >> mnOrientation >> mnButtonFlags >> mnShortcut >> mnShortcutEA >> mnTextLen >> mnFormatSize; 266 } 267 268 sal_uInt8 BiffObjTextModel::getHorAlign() const 269 { 270 return extractValue< sal_uInt8 >( mnFlags, 1, 3 ); 271 } 272 273 sal_uInt8 BiffObjTextModel::getVerAlign() const 274 { 275 return extractValue< sal_uInt8 >( mnFlags, 4, 3 ); 276 } 277 278 // ============================================================================ 279 // BIFF drawing objects 280 // ============================================================================ 281 282 BiffDrawingObjectContainer::BiffDrawingObjectContainer() 283 { 284 } 285 286 void BiffDrawingObjectContainer::append( const BiffDrawingObjectRef& rxDrawingObj ) 287 { 288 maObjects.push_back( rxDrawingObj ); 289 } 290 291 void BiffDrawingObjectContainer::insertGrouped( const BiffDrawingObjectRef& rxDrawingObj ) 292 { 293 if( !maObjects.empty() ) 294 if( BiffGroupObject* pGroupObj = dynamic_cast< BiffGroupObject* >( maObjects.back().get() ) ) 295 if( pGroupObj->tryInsert( rxDrawingObj ) ) 296 return; 297 maObjects.push_back( rxDrawingObj ); 298 } 299 300 void BiffDrawingObjectContainer::convertAndInsert( BiffDrawingBase& rDrawing, const Reference< XShapes >& rxShapes, const Rectangle* pParentRect ) const 301 { 302 maObjects.forEachMem( &BiffDrawingObjectBase::convertAndInsert, ::boost::ref( rDrawing ), ::boost::cref( rxShapes ), pParentRect ); 303 } 304 305 // ============================================================================ 306 307 BiffDrawingObjectBase::BiffDrawingObjectBase( const WorksheetHelper& rHelper ) : 308 WorksheetHelper( rHelper ), 309 maAnchor( rHelper ), 310 mnDffShapeId( 0 ), 311 mnDffFlags( 0 ), 312 mnObjId( BIFF_OBJ_INVALID_ID ), 313 mnObjType( BIFF_OBJTYPE_UNKNOWN ), 314 mbHasAnchor( false ), 315 mbHidden( false ), 316 mbVisible( true ), 317 mbPrintable( true ), 318 mbAreaObj( false ), 319 mbAutoMargin( true ), 320 mbSimpleMacro( true ), 321 mbProcessShape( true ), 322 mbInsertShape( true ), 323 mbCustomDff( false ) 324 { 325 } 326 327 BiffDrawingObjectBase::~BiffDrawingObjectBase() 328 { 329 } 330 331 /*static*/ BiffDrawingObjectRef BiffDrawingObjectBase::importObjBiff3( const WorksheetHelper& rHelper, BiffInputStream& rStrm ) 332 { 333 BiffDrawingObjectRef xDrawingObj; 334 335 if( rStrm.getRemaining() >= 30 ) 336 { 337 sal_uInt16 nObjType; 338 rStrm.skip( 4 ); 339 rStrm >> nObjType; 340 switch( nObjType ) 341 { 342 case BIFF_OBJTYPE_GROUP: xDrawingObj.reset( new BiffGroupObject( rHelper ) ); break; 343 case BIFF_OBJTYPE_LINE: xDrawingObj.reset( new BiffLineObject( rHelper ) ); break; 344 case BIFF_OBJTYPE_RECTANGLE: xDrawingObj.reset( new BiffRectObject( rHelper ) ); break; 345 case BIFF_OBJTYPE_OVAL: xDrawingObj.reset( new BiffOvalObject( rHelper ) ); break; 346 case BIFF_OBJTYPE_ARC: xDrawingObj.reset( new BiffArcObject( rHelper ) ); break; 347 #if 0 348 case BIFF_OBJTYPE_CHART: xDrawingObj.reset( new XclImpChartObj( rHelper ) ); break; 349 case BIFF_OBJTYPE_TEXT: xDrawingObj.reset( new XclImpTextObj( rHelper ) ); break; 350 case BIFF_OBJTYPE_BUTTON: xDrawingObj.reset( new XclImpButtonObj( rHelper ) ); break; 351 case BIFF_OBJTYPE_PICTURE: xDrawingObj.reset( new XclImpPictureObj( rHelper ) ); break; 352 #endif 353 default: 354 #if 0 355 OSL_ENSURE( false, "BiffDrawingObjectBase::importObjBiff3 - unknown object type" ); 356 #endif 357 xDrawingObj.reset( new BiffPlaceholderObject( rHelper ) ); 358 } 359 } 360 361 xDrawingObj->importObjBiff3( rStrm ); 362 return xDrawingObj; 363 } 364 365 /*static*/ BiffDrawingObjectRef BiffDrawingObjectBase::importObjBiff4( const WorksheetHelper& rHelper, BiffInputStream& rStrm ) 366 { 367 BiffDrawingObjectRef xDrawingObj; 368 369 if( rStrm.getRemaining() >= 30 ) 370 { 371 sal_uInt16 nObjType; 372 rStrm.skip( 4 ); 373 rStrm >> nObjType; 374 switch( nObjType ) 375 { 376 case BIFF_OBJTYPE_GROUP: xDrawingObj.reset( new BiffGroupObject( rHelper ) ); break; 377 case BIFF_OBJTYPE_LINE: xDrawingObj.reset( new BiffLineObject( rHelper ) ); break; 378 case BIFF_OBJTYPE_RECTANGLE: xDrawingObj.reset( new BiffRectObject( rHelper ) ); break; 379 case BIFF_OBJTYPE_OVAL: xDrawingObj.reset( new BiffOvalObject( rHelper ) ); break; 380 case BIFF_OBJTYPE_ARC: xDrawingObj.reset( new BiffArcObject( rHelper ) ); break; 381 case BIFF_OBJTYPE_POLYGON: xDrawingObj.reset( new BiffPolygonObject( rHelper ) ); break; 382 #if 0 383 case BIFF_OBJTYPE_CHART: xDrawingObj.reset( new XclImpChartObj( rHelper ) ); break; 384 case BIFF_OBJTYPE_TEXT: xDrawingObj.reset( new XclImpTextObj( rHelper ) ); break; 385 case BIFF_OBJTYPE_BUTTON: xDrawingObj.reset( new XclImpButtonObj( rHelper ) ); break; 386 case BIFF_OBJTYPE_PICTURE: xDrawingObj.reset( new XclImpPictureObj( rHelper ) ); break; 387 #endif 388 default: 389 #if 0 390 OSL_ENSURE( false, "BiffDrawingObjectBase::importObjBiff4 - unknown object type" ); 391 #endif 392 xDrawingObj.reset( new BiffPlaceholderObject( rHelper ) ); 393 } 394 } 395 396 xDrawingObj->importObjBiff4( rStrm ); 397 return xDrawingObj; 398 } 399 400 /*static*/ BiffDrawingObjectRef BiffDrawingObjectBase::importObjBiff5( const WorksheetHelper& rHelper, BiffInputStream& rStrm ) 401 { 402 BiffDrawingObjectRef xDrawingObj; 403 404 if( rStrm.getRemaining() >= 34 ) 405 { 406 sal_uInt16 nObjType; 407 rStrm.skip( 4 ); 408 rStrm >> nObjType; 409 switch( nObjType ) 410 { 411 case BIFF_OBJTYPE_GROUP: xDrawingObj.reset( new BiffGroupObject( rHelper ) ); break; 412 case BIFF_OBJTYPE_LINE: xDrawingObj.reset( new BiffLineObject( rHelper ) ); break; 413 case BIFF_OBJTYPE_RECTANGLE: xDrawingObj.reset( new BiffRectObject( rHelper ) ); break; 414 case BIFF_OBJTYPE_OVAL: xDrawingObj.reset( new BiffOvalObject( rHelper ) ); break; 415 case BIFF_OBJTYPE_ARC: xDrawingObj.reset( new BiffArcObject( rHelper ) ); break; 416 case BIFF_OBJTYPE_POLYGON: xDrawingObj.reset( new BiffPolygonObject( rHelper ) ); break; 417 #if 0 418 case BIFF_OBJTYPE_CHART: xDrawingObj.reset( new XclImpChartObj( rHelper ) ); break; 419 case BIFF_OBJTYPE_TEXT: xDrawingObj.reset( new XclImpTextObj( rHelper ) ); break; 420 case BIFF_OBJTYPE_BUTTON: xDrawingObj.reset( new XclImpButtonObj( rHelper ) ); break; 421 case BIFF_OBJTYPE_PICTURE: xDrawingObj.reset( new XclImpPictureObj( rHelper ) ); break; 422 case BIFF_OBJTYPE_CHECKBOX: xDrawingObj.reset( new XclImpCheckBoxObj( rHelper ) ); break; 423 case BIFF_OBJTYPE_OPTIONBUTTON: xDrawingObj.reset( new XclImpOptionButtonObj( rHelper ) ); break; 424 case BIFF_OBJTYPE_EDIT: xDrawingObj.reset( new XclImpEditObj( rHelper ) ); break; 425 case BIFF_OBJTYPE_LABEL: xDrawingObj.reset( new XclImpLabelObj( rHelper ) ); break; 426 case BIFF_OBJTYPE_DIALOG: xDrawingObj.reset( new XclImpDialogObj( rHelper ) ); break; 427 case BIFF_OBJTYPE_SPIN: xDrawingObj.reset( new XclImpSpinButtonObj( rHelper ) ); break; 428 case BIFF_OBJTYPE_SCROLLBAR: xDrawingObj.reset( new XclImpScrollBarObj( rHelper ) ); break; 429 case BIFF_OBJTYPE_LISTBOX: xDrawingObj.reset( new XclImpListBoxObj( rHelper ) ); break; 430 case BIFF_OBJTYPE_GROUPBOX: xDrawingObj.reset( new XclImpGroupBoxObj( rHelper ) ); break; 431 case BIFF_OBJTYPE_DROPDOWN: xDrawingObj.reset( new XclImpDropDownObj( rHelper ) ); break; 432 #endif 433 default: 434 #if 0 435 OSL_ENSURE( false, "BiffDrawingObjectBase::importObjBiff5 - unknown object type" ); 436 #endif 437 xDrawingObj.reset( new BiffPlaceholderObject( rHelper ) ); 438 } 439 } 440 441 xDrawingObj->importObjBiff5( rStrm ); 442 return xDrawingObj; 443 } 444 445 /*static*/ BiffDrawingObjectRef BiffDrawingObjectBase::importObjBiff8( const WorksheetHelper& rHelper, BiffInputStream& rStrm ) 446 { 447 BiffDrawingObjectRef xDrawingObj; 448 449 if( rStrm.getRemaining() >= 10 ) 450 { 451 sal_uInt16 nSubRecId, nSubRecSize, nObjType; 452 rStrm >> nSubRecId >> nSubRecSize >> nObjType; 453 OSL_ENSURE( nSubRecId == BIFF_ID_OBJCMO, "BiffDrawingObjectBase::importObjBiff8 - OBJCMO subrecord expected" ); 454 if( (nSubRecId == BIFF_ID_OBJCMO) && (nSubRecSize >= 6) ) 455 { 456 switch( nObjType ) 457 { 458 #if 0 459 // in BIFF8, all simple objects support text 460 case BIFF_OBJTYPE_LINE: 461 case BIFF_OBJTYPE_ARC: 462 xDrawingObj.reset( new XclImpTextObj( rHelper ) ); 463 // lines and arcs may be 2-dimensional 464 xDrawingObj->setAreaObj( false ); 465 break; 466 467 // in BIFF8, all simple objects support text 468 case BIFF_OBJTYPE_RECTANGLE: 469 case BIFF_OBJTYPE_OVAL: 470 case BIFF_OBJTYPE_POLYGON: 471 case BIFF_OBJTYPE_DRAWING: 472 case BIFF_OBJTYPE_TEXT: 473 xDrawingObj.reset( new XclImpTextObj( rHelper ) ); 474 break; 475 #endif 476 477 case BIFF_OBJTYPE_GROUP: xDrawingObj.reset( new BiffGroupObject( rHelper ) ); break; 478 #if 0 479 case BIFF_OBJTYPE_CHART: xDrawingObj.reset( new XclImpChartObj( rHelper ) ); break; 480 case BIFF_OBJTYPE_BUTTON: xDrawingObj.reset( new XclImpButtonObj( rHelper ) ); break; 481 case BIFF_OBJTYPE_PICTURE: xDrawingObj.reset( new XclImpPictureObj( rHelper ) ); break; 482 case BIFF_OBJTYPE_CHECKBOX: xDrawingObj.reset( new XclImpCheckBoxObj( rHelper ) ); break; 483 case BIFF_OBJTYPE_OPTIONBUTTON: xDrawingObj.reset( new XclImpOptionButtonObj( rHelper ) ); break; 484 case BIFF_OBJTYPE_EDIT: xDrawingObj.reset( new XclImpEditObj( rHelper ) ); break; 485 case BIFF_OBJTYPE_LABEL: xDrawingObj.reset( new XclImpLabelObj( rHelper ) ); break; 486 case BIFF_OBJTYPE_DIALOG: xDrawingObj.reset( new XclImpDialogObj( rHelper ) ); break; 487 case BIFF_OBJTYPE_SPIN: xDrawingObj.reset( new XclImpSpinButtonObj( rHelper ) ); break; 488 case BIFF_OBJTYPE_SCROLLBAR: xDrawingObj.reset( new XclImpScrollBarObj( rHelper ) ); break; 489 case BIFF_OBJTYPE_LISTBOX: xDrawingObj.reset( new XclImpListBoxObj( rHelper ) ); break; 490 case BIFF_OBJTYPE_GROUPBOX: xDrawingObj.reset( new XclImpGroupBoxObj( rHelper ) ); break; 491 case BIFF_OBJTYPE_DROPDOWN: xDrawingObj.reset( new XclImpDropDownObj( rHelper ) ); break; 492 case BIFF_OBJTYPE_NOTE: xDrawingObj.reset( new XclImpNoteObj( rHelper ) ); break; 493 #endif 494 495 default: 496 #if 0 497 OSL_ENSURE( false, "BiffDrawingObjectBase::importObjBiff8 - unknown object type" ); 498 #endif 499 xDrawingObj.reset( new BiffPlaceholderObject( rHelper ) ); 500 } 501 } 502 } 503 504 xDrawingObj->importObjBiff8( rStrm ); 505 return xDrawingObj; 506 } 507 508 Reference< XShape > BiffDrawingObjectBase::convertAndInsert( BiffDrawingBase& rDrawing, 509 const Reference< XShapes >& rxShapes, const Rectangle* pParentRect ) const 510 { 511 Reference< XShape > xShape; 512 if( rxShapes.is() && mbProcessShape && !mbHidden ) // TODO: support for hidden objects? 513 { 514 // base class 'ShapeAnchor' calculates the shape rectangle in 1/100 mm 515 // in BIFF3-BIFF5, all shapes have absolute anchor (also children of group shapes) 516 Rectangle aShapeRect = maAnchor.calcAnchorRectHmm( getDrawPageSize() ); 517 518 // convert the shape, if the calculated rectangle is not empty 519 bool bHasWidth = aShapeRect.Width > 0; 520 bool bHasHeight = aShapeRect.Height > 0; 521 if( mbAreaObj ? (bHasWidth && bHasHeight) : (bHasWidth || bHasHeight) ) 522 { 523 xShape = implConvertAndInsert( rDrawing, rxShapes, aShapeRect ); 524 /* Notify the drawing that a new shape has been inserted (but not 525 for children of group shapes). For convenience, pass the 526 rectangle that contains position and size of the shape. */ 527 if( !pParentRect && xShape.is() ) 528 rDrawing.notifyShapeInserted( xShape, aShapeRect ); 529 } 530 } 531 return xShape; 532 } 533 534 // protected ------------------------------------------------------------------ 535 536 void BiffDrawingObjectBase::readNameBiff5( BiffInputStream& rStrm, sal_uInt16 nNameLen ) 537 { 538 maObjName = OUString(); 539 if( nNameLen > 0 ) 540 { 541 // name length field is repeated before the name 542 maObjName = rStrm.readByteStringUC( false, getTextEncoding() ); 543 // skip padding byte for word boundaries 544 rStrm.alignToBlock( 2 ); 545 } 546 } 547 548 void BiffDrawingObjectBase::readMacroBiff3( BiffInputStream& rStrm, sal_uInt16 nMacroSize ) 549 { 550 maMacroName = OUString(); 551 rStrm.skip( nMacroSize ); 552 // skip padding byte for word boundaries, not contained in nMacroSize 553 rStrm.alignToBlock( 2 ); 554 } 555 556 void BiffDrawingObjectBase::readMacroBiff4( BiffInputStream& rStrm, sal_uInt16 nMacroSize ) 557 { 558 maMacroName = OUString(); 559 rStrm.skip( nMacroSize ); 560 } 561 562 void BiffDrawingObjectBase::readMacroBiff5( BiffInputStream& rStrm, sal_uInt16 nMacroSize ) 563 { 564 maMacroName = OUString(); 565 rStrm.skip( nMacroSize ); 566 } 567 568 void BiffDrawingObjectBase::readMacroBiff8( BiffInputStream& rStrm ) 569 { 570 maMacroName = OUString(); 571 if( rStrm.getRemaining() > 6 ) 572 { 573 // macro is stored in a tNameXR token containing a link to a defined name 574 sal_uInt16 nFmlaSize; 575 rStrm >> nFmlaSize; 576 rStrm.skip( 4 ); 577 OSL_ENSURE( nFmlaSize == 7, "BiffDrawingObjectBase::readMacroBiff8 - unexpected formula size" ); 578 if( nFmlaSize == 7 ) 579 { 580 sal_uInt8 nTokenId; 581 sal_uInt16 nExtLinkId, nExtNameId; 582 rStrm >> nTokenId >> nExtLinkId >> nExtNameId; 583 #if 0 584 OSL_ENSURE( nTokenId == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ), 585 "BiffDrawingObjectBase::readMacroBiff8 - tNameXR token expected" ); 586 if( nTokenId == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ) ) 587 maMacroName = GetLinkManager().GetMacroName( nExtLinkId, nExtNameId ); 588 #endif 589 } 590 } 591 } 592 593 void BiffDrawingObjectBase::convertLineProperties( ShapePropertyMap& rPropMap, const BiffObjLineModel& rLineModel, sal_uInt16 nArrows ) const 594 { 595 if( rLineModel.mbAuto ) 596 { 597 BiffObjLineModel aAutoModel; 598 aAutoModel.mbAuto = false; 599 convertLineProperties( rPropMap, aAutoModel, nArrows ); 600 return; 601 } 602 603 /* Convert line formatting to DrawingML line formatting and let the 604 DrawingML code do the hard work. */ 605 LineProperties aLineProps; 606 607 if( rLineModel.mnStyle == BIFF_OBJ_LINE_NONE ) 608 { 609 aLineProps.maLineFill.moFillType = XML_noFill; 610 } 611 else 612 { 613 aLineProps.maLineFill.moFillType = XML_solidFill; 614 aLineProps.maLineFill.maFillColor.setPaletteClr( rLineModel.mnColorIdx ); 615 aLineProps.moLineCompound = XML_sng; 616 aLineProps.moLineCap = XML_flat; 617 aLineProps.moLineJoint = XML_round; 618 619 // line width: use 0.35 mm per BIFF line width step 620 sal_Int32 nLineWidth = 0; 621 switch( rLineModel.mnWidth ) 622 { 623 default: 624 case BIFF_OBJ_LINE_HAIR: nLineWidth = 0; break; 625 case BIFF_OBJ_LINE_THIN: nLineWidth = 20; break; 626 case BIFF_OBJ_LINE_MEDIUM: nLineWidth = 40; break; 627 case BIFF_OBJ_LINE_THICK: nLineWidth = 60; break; 628 } 629 aLineProps.moLineWidth = getLimitedValue< sal_Int32, sal_Int64 >( convertHmmToEmu( nLineWidth ), 0, SAL_MAX_INT32 ); 630 631 // dash style and transparency 632 switch( rLineModel.mnStyle ) 633 { 634 default: 635 case BIFF_OBJ_LINE_SOLID: 636 aLineProps.moPresetDash = XML_solid; 637 break; 638 case BIFF_OBJ_LINE_DASH: 639 aLineProps.moPresetDash = XML_lgDash; 640 break; 641 case BIFF_OBJ_LINE_DOT: 642 aLineProps.moPresetDash = XML_dot; 643 break; 644 case BIFF_OBJ_LINE_DASHDOT: 645 aLineProps.moPresetDash = XML_lgDashDot; 646 break; 647 case BIFF_OBJ_LINE_DASHDOTDOT: 648 aLineProps.moPresetDash = XML_lgDashDotDot; 649 break; 650 case BIFF_OBJ_LINE_MEDTRANS: 651 aLineProps.moPresetDash = XML_solid; 652 aLineProps.maLineFill.maFillColor.addTransformation( XML_alpha, 50 * PER_PERCENT ); 653 break; 654 case BIFF_OBJ_LINE_DARKTRANS: 655 aLineProps.moPresetDash = XML_solid; 656 aLineProps.maLineFill.maFillColor.addTransformation( XML_alpha, 75 * PER_PERCENT ); 657 break; 658 case BIFF_OBJ_LINE_LIGHTTRANS: 659 aLineProps.moPresetDash = XML_solid; 660 aLineProps.maLineFill.maFillColor.addTransformation( XML_alpha, 25 * PER_PERCENT ); 661 break; 662 } 663 664 // line ends 665 bool bLineStart = false; 666 bool bLineEnd = false; 667 bool bFilled = false; 668 switch( extractValue< sal_uInt8 >( nArrows, 0, 4 ) ) 669 { 670 case BIFF_OBJ_ARROW_OPEN: bLineStart = false; bLineEnd = true; bFilled = false; break; 671 case BIFF_OBJ_ARROW_OPENBOTH: bLineStart = true; bLineEnd = true; bFilled = false; break; 672 case BIFF_OBJ_ARROW_FILLED: bLineStart = false; bLineEnd = true; bFilled = true; break; 673 case BIFF_OBJ_ARROW_FILLEDBOTH: bLineStart = true; bLineEnd = true; bFilled = true; break; 674 } 675 if( bLineStart || bLineEnd ) 676 { 677 // arrow type (open or closed) 678 sal_Int32 nArrowType = bFilled ? XML_triangle : XML_arrow; 679 aLineProps.maStartArrow.moArrowType = bLineStart ? nArrowType : XML_none; 680 aLineProps.maEndArrow.moArrowType = bLineEnd ? nArrowType : XML_none; 681 682 // arrow width 683 sal_Int32 nArrowWidth = XML_med; 684 switch( extractValue< sal_uInt8 >( nArrows, 4, 4 ) ) 685 { 686 case BIFF_OBJ_ARROW_NARROW: nArrowWidth = XML_sm; break; 687 case BIFF_OBJ_ARROW_MEDIUM: nArrowWidth = XML_med; break; 688 case BIFF_OBJ_ARROW_WIDE: nArrowWidth = XML_lg; break; 689 } 690 aLineProps.maStartArrow.moArrowWidth = aLineProps.maEndArrow.moArrowWidth = nArrowWidth; 691 692 // arrow length 693 sal_Int32 nArrowLength = XML_med; 694 switch( extractValue< sal_uInt8 >( nArrows, 8, 4 ) ) 695 { 696 case BIFF_OBJ_ARROW_NARROW: nArrowLength = XML_sm; break; 697 case BIFF_OBJ_ARROW_MEDIUM: nArrowLength = XML_med; break; 698 case BIFF_OBJ_ARROW_WIDE: nArrowLength = XML_lg; break; 699 } 700 aLineProps.maStartArrow.moArrowLength = aLineProps.maEndArrow.moArrowLength = nArrowLength; 701 } 702 } 703 704 aLineProps.pushToPropMap( rPropMap, getBaseFilter().getGraphicHelper() ); 705 } 706 707 void BiffDrawingObjectBase::convertFillProperties( ShapePropertyMap& rPropMap, const BiffObjFillModel& rFillModel ) const 708 { 709 if( rFillModel.mbAuto ) 710 { 711 BiffObjFillModel aAutoModel; 712 aAutoModel.mbAuto = false; 713 convertFillProperties( rPropMap, aAutoModel ); 714 return; 715 } 716 717 /* Convert fill formatting to DrawingML fill formatting and let the 718 DrawingML code do the hard work. */ 719 FillProperties aFillProps; 720 721 if( rFillModel.mnPattern == BIFF_OBJ_PATT_NONE ) 722 { 723 aFillProps.moFillType = XML_noFill; 724 } 725 else 726 { 727 const sal_Int32 spnPatternPresets[] = { 728 XML_TOKEN_INVALID, XML_TOKEN_INVALID, XML_pct50, XML_pct50, XML_pct25, 729 XML_dkHorz, XML_dkVert, XML_dkDnDiag, XML_dkUpDiag, XML_smCheck, XML_trellis, 730 XML_ltHorz, XML_ltVert, XML_ltDnDiag, XML_ltUpDiag, XML_smGrid, XML_diagCross, 731 XML_pct20, XML_pct10 }; 732 sal_Int32 nPatternPreset = STATIC_ARRAY_SELECT( spnPatternPresets, rFillModel.mnPattern, XML_TOKEN_INVALID ); 733 if( nPatternPreset == XML_TOKEN_INVALID ) 734 { 735 aFillProps.moFillType = XML_solidFill; 736 aFillProps.maFillColor.setPaletteClr( rFillModel.mnPattColorIdx ); 737 } 738 else 739 { 740 aFillProps.moFillType = XML_pattFill; 741 aFillProps.maPatternProps.maPattFgColor.setPaletteClr( rFillModel.mnPattColorIdx ); 742 aFillProps.maPatternProps.maPattBgColor.setPaletteClr( rFillModel.mnBackColorIdx ); 743 aFillProps.maPatternProps.moPattPreset = nPatternPreset; 744 } 745 #if 0 746 static const sal_uInt8 sppnPatterns[][ 8 ] = 747 { 748 { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 }, 749 { 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD }, 750 { 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22 }, 751 { 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00 }, 752 { 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC }, 753 { 0x33, 0x66, 0xCC, 0x99, 0x33, 0x66, 0xCC, 0x99 }, 754 { 0xCC, 0x66, 0x33, 0x99, 0xCC, 0x66, 0x33, 0x99 }, 755 { 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33 }, 756 { 0xCC, 0xFF, 0x33, 0xFF, 0xCC, 0xFF, 0x33, 0xFF }, 757 { 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 758 { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88 }, 759 { 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 }, 760 { 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 }, 761 { 0xFF, 0x11, 0x11, 0x11, 0xFF, 0x11, 0x11, 0x11 }, 762 { 0xAA, 0x44, 0xAA, 0x11, 0xAA, 0x44, 0xAA, 0x11 }, 763 { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 }, 764 { 0x80, 0x00, 0x08, 0x00, 0x80, 0x00, 0x08, 0x00 } 765 }; 766 const sal_uInt8* const pnPattern = sppnPatterns[ ::std::min< size_t >( rFillData.mnPattern - 2, STATIC_ARRAY_SIZE( sppnPatterns ) ) ]; 767 // create 2-colored 8x8 DIB 768 SvMemoryStream aMemStrm; 769 // { 0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x01, 0x00, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00 } 770 aMemStrm << sal_uInt32( 12 ) << sal_Int16( 8 ) << sal_Int16( 8 ) << sal_uInt16( 1 ) << sal_uInt16( 1 ); 771 aMemStrm << sal_uInt8( 0xFF ) << sal_uInt8( 0xFF ) << sal_uInt8( 0xFF ); 772 aMemStrm << sal_uInt8( 0x00 ) << sal_uInt8( 0x00 ) << sal_uInt8( 0x00 ); 773 for( size_t nIdx = 0; nIdx < 8; ++nIdx ) 774 aMemStrm << sal_uInt32( pnPattern[ nIdx ] ); // 32-bit little-endian 775 aMemStrm.Seek( STREAM_SEEK_TO_BEGIN ); 776 Bitmap aBitmap; 777 aBitmap.Read( aMemStrm, FALSE ); 778 XOBitmap aXOBitmap( aBitmap ); 779 aXOBitmap.Bitmap2Array(); 780 aXOBitmap.SetBitmapType( XBITMAP_8X8 ); 781 if( aXOBitmap.GetBackgroundColor().GetColor() == COL_BLACK ) 782 ::std::swap( aPattColor, aBackColor ); 783 aXOBitmap.SetPixelColor( aPattColor ); 784 aXOBitmap.SetBackgroundColor( aBackColor ); 785 rSdrObj.SetMergedItem( XFillStyleItem( XFILL_BITMAP ) ); 786 rSdrObj.SetMergedItem( XFillBitmapItem( EMPTY_STRING, aXOBitmap ) ); 787 #endif 788 } 789 790 aFillProps.pushToPropMap( rPropMap, getBaseFilter().getGraphicHelper() ); 791 } 792 793 void BiffDrawingObjectBase::convertFrameProperties( ShapePropertyMap& /*rPropMap*/, sal_uInt16 /*nFrameFlags*/ ) const 794 { 795 } 796 797 void BiffDrawingObjectBase::implReadObjBiff3( BiffInputStream& /*rStrm*/, sal_uInt16 /*nMacroSize*/ ) 798 { 799 } 800 801 void BiffDrawingObjectBase::implReadObjBiff4( BiffInputStream& /*rStrm*/, sal_uInt16 /*nMacroSize*/ ) 802 { 803 } 804 805 void BiffDrawingObjectBase::implReadObjBiff5( BiffInputStream& /*rStrm*/, sal_uInt16 /*nNameLen*/, sal_uInt16 /*nMacroSize*/ ) 806 { 807 } 808 809 void BiffDrawingObjectBase::implReadObjBiff8SubRec( BiffInputStream& /*rStrm*/, sal_uInt16 /*nSubRecId*/, sal_uInt16 /*nSubRecSize*/ ) 810 { 811 } 812 813 // private -------------------------------------------------------------------- 814 815 void BiffDrawingObjectBase::importObjBiff3( BiffInputStream& rStrm ) 816 { 817 // back to offset 4 (ignore object count field) 818 rStrm.seek( 4 ); 819 820 sal_uInt16 nObjFlags, nMacroSize; 821 rStrm >> mnObjType >> mnObjId >> nObjFlags >> maAnchor >> nMacroSize; 822 rStrm.skip( 2 ); 823 824 mbHasAnchor = true; 825 mbHidden = getFlag( nObjFlags, BIFF_OBJ_HIDDEN ); 826 mbVisible = getFlag( nObjFlags, BIFF_OBJ_VISIBLE ); 827 implReadObjBiff3( rStrm, nMacroSize ); 828 } 829 830 void BiffDrawingObjectBase::importObjBiff4( BiffInputStream& rStrm ) 831 { 832 // back to offset 4 (ignore object count field) 833 rStrm.seek( 4 ); 834 835 sal_uInt16 nObjFlags, nMacroSize; 836 rStrm >> mnObjType >> mnObjId >> nObjFlags >> maAnchor >> nMacroSize; 837 rStrm.skip( 2 ); 838 839 mbHasAnchor = true; 840 mbHidden = getFlag( nObjFlags, BIFF_OBJ_HIDDEN ); 841 mbVisible = getFlag( nObjFlags, BIFF_OBJ_VISIBLE ); 842 mbPrintable = getFlag( nObjFlags, BIFF_OBJ_PRINTABLE ); 843 implReadObjBiff4( rStrm, nMacroSize ); 844 } 845 846 void BiffDrawingObjectBase::importObjBiff5( BiffInputStream& rStrm ) 847 { 848 // back to offset 4 (ignore object count field) 849 rStrm.seek( 4 ); 850 851 sal_uInt16 nObjFlags, nMacroSize, nNameLen; 852 rStrm >> mnObjType >> mnObjId >> nObjFlags >> maAnchor >> nMacroSize; 853 rStrm.skip( 2 ); 854 rStrm >> nNameLen; 855 rStrm.skip( 2 ); 856 857 mbHasAnchor = true; 858 mbHidden = getFlag( nObjFlags, BIFF_OBJ_HIDDEN ); 859 mbVisible = getFlag( nObjFlags, BIFF_OBJ_VISIBLE ); 860 mbPrintable = getFlag( nObjFlags, BIFF_OBJ_PRINTABLE ); 861 implReadObjBiff5( rStrm, nNameLen, nMacroSize ); 862 } 863 864 void BiffDrawingObjectBase::importObjBiff8( BiffInputStream& rStrm ) 865 { 866 // back to beginning 867 rStrm.seekToStart(); 868 869 bool bLoop = true; 870 while( bLoop && (rStrm.getRemaining() >= 4) ) 871 { 872 sal_uInt16 nSubRecId, nSubRecSize; 873 rStrm >> nSubRecId >> nSubRecSize; 874 sal_Int64 nStrmPos = rStrm.tell(); 875 // sometimes the last subrecord has an invalid length (OBJLBSDATA) -> min() 876 nSubRecSize = static_cast< sal_uInt16 >( ::std::min< sal_Int64 >( nSubRecSize, rStrm.getRemaining() ) ); 877 878 switch( nSubRecId ) 879 { 880 case BIFF_ID_OBJCMO: 881 OSL_ENSURE( rStrm.tell() == 4, "BiffDrawingObjectBase::importObjBiff8 - unexpected OBJCMO subrecord" ); 882 if( (rStrm.tell() == 4) && (nSubRecSize >= 6) ) 883 { 884 sal_uInt16 nObjFlags; 885 rStrm >> mnObjType >> mnObjId >> nObjFlags; 886 mbPrintable = getFlag( nObjFlags, BIFF_OBJCMO_PRINTABLE ); 887 } 888 break; 889 case BIFF_ID_OBJMACRO: 890 readMacroBiff8( rStrm ); 891 break; 892 case BIFF_ID_OBJEND: 893 bLoop = false; 894 break; 895 default: 896 implReadObjBiff8SubRec( rStrm, nSubRecId, nSubRecSize ); 897 } 898 899 // seek to end of subrecord 900 rStrm.seek( nStrmPos + nSubRecSize ); 901 } 902 903 /* Call doReadObj8SubRec() with BIFF_ID_OBJEND for further stream 904 processing (e.g. charts), even if the OBJEND subrecord is missing. */ 905 implReadObjBiff8SubRec( rStrm, BIFF_ID_OBJEND, 0 ); 906 907 /* Pictures that Excel reads from BIFF5 and writes to BIFF8 still have the 908 IMGDATA record following the OBJ record (but they use the image data 909 stored in DFF). The IMGDATA record may be continued by several CONTINUE 910 records. But the last CONTINUE record may be in fact an MSODRAWING 911 record that contains the DFF data of the next drawing object! So we 912 have to skip just enough CONTINUE records to look at the next 913 MSODRAWING/CONTINUE record. */ 914 if( (rStrm.getNextRecId() == BIFF3_ID_IMGDATA) && rStrm.startNextRecord() ) 915 { 916 rStrm.skip( 4 ); 917 sal_Int64 nDataSize = rStrm.readuInt32(); 918 nDataSize -= rStrm.getRemaining(); 919 // skip following CONTINUE records until IMGDATA ends 920 while( (nDataSize > 0) && (rStrm.getNextRecId() == BIFF_ID_CONT) && rStrm.startNextRecord() ) 921 { 922 OSL_ENSURE( nDataSize >= rStrm.getRemaining(), "BiffDrawingObjectBase::importObjBiff8 - CONTINUE too long" ); 923 nDataSize -= ::std::min( rStrm.getRemaining(), nDataSize ); 924 } 925 OSL_ENSURE( nDataSize == 0, "BiffDrawingObjectBase::importObjBiff8 - missing CONTINUE records" ); 926 // next record may be MSODRAWING or CONTINUE or anything else 927 } 928 } 929 930 // ============================================================================ 931 932 BiffPlaceholderObject::BiffPlaceholderObject( const WorksheetHelper& rHelper ) : 933 BiffDrawingObjectBase( rHelper ) 934 { 935 setProcessShape( false ); 936 } 937 938 Reference< XShape > BiffPlaceholderObject::implConvertAndInsert( BiffDrawingBase& /*rDrawing*/, 939 const Reference< XShapes >& /*rxShapes*/, const Rectangle& /*rShapeRect*/ ) const 940 { 941 return Reference< XShape >(); 942 } 943 944 // ============================================================================ 945 946 BiffGroupObject::BiffGroupObject( const WorksheetHelper& rHelper ) : 947 BiffDrawingObjectBase( rHelper ), 948 mnFirstUngrouped( BIFF_OBJ_INVALID_ID ) 949 { 950 } 951 952 bool BiffGroupObject::tryInsert( const BiffDrawingObjectRef& rxDrawingObj ) 953 { 954 if( rxDrawingObj->getObjId() == mnFirstUngrouped ) 955 return false; 956 // insert into own list or into nested group 957 maChildren.insertGrouped( rxDrawingObj ); 958 return true; 959 } 960 961 void BiffGroupObject::implReadObjBiff3( BiffInputStream& rStrm, sal_uInt16 nMacroSize ) 962 { 963 rStrm.skip( 4 ); 964 rStrm >> mnFirstUngrouped; 965 rStrm.skip( 16 ); 966 readMacroBiff3( rStrm, nMacroSize ); 967 } 968 969 void BiffGroupObject::implReadObjBiff4( BiffInputStream& rStrm, sal_uInt16 nMacroSize ) 970 { 971 rStrm.skip( 4 ); 972 rStrm >> mnFirstUngrouped; 973 rStrm.skip( 16 ); 974 readMacroBiff4( rStrm, nMacroSize ); 975 } 976 977 void BiffGroupObject::implReadObjBiff5( BiffInputStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) 978 { 979 rStrm.skip( 4 ); 980 rStrm >> mnFirstUngrouped; 981 rStrm.skip( 16 ); 982 readNameBiff5( rStrm, nNameLen ); 983 readMacroBiff5( rStrm, nMacroSize ); 984 } 985 986 Reference< XShape > BiffGroupObject::implConvertAndInsert( BiffDrawingBase& rDrawing, 987 const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const 988 { 989 Reference< XShape > xGroupShape; 990 if( !maChildren.empty() ) try 991 { 992 xGroupShape = rDrawing.createAndInsertXShape( CREATE_OUSTRING( "com.sun.star.drawing.GroupShape" ), rxShapes, rShapeRect ); 993 Reference< XShapes > xChildShapes( xGroupShape, UNO_QUERY_THROW ); 994 maChildren.convertAndInsert( rDrawing, xChildShapes, &rShapeRect ); 995 // no child shape has been created - delete the group shape 996 if( !xChildShapes->hasElements() ) 997 { 998 rxShapes->remove( xGroupShape ); 999 xGroupShape.clear(); 1000 } 1001 } 1002 catch( Exception& ) 1003 { 1004 } 1005 return xGroupShape; 1006 } 1007 1008 // ============================================================================ 1009 1010 BiffLineObject::BiffLineObject( const WorksheetHelper& rHelper ) : 1011 BiffDrawingObjectBase( rHelper ), 1012 mnArrows( 0 ), 1013 mnStartPoint( BIFF_OBJ_LINE_TL ) 1014 { 1015 setAreaObj( false ); 1016 } 1017 1018 void BiffLineObject::implReadObjBiff3( BiffInputStream& rStrm, sal_uInt16 nMacroSize ) 1019 { 1020 rStrm >> maLineModel >> mnArrows >> mnStartPoint; 1021 rStrm.skip( 1 ); 1022 readMacroBiff3( rStrm, nMacroSize ); 1023 } 1024 1025 void BiffLineObject::implReadObjBiff4( BiffInputStream& rStrm, sal_uInt16 nMacroSize ) 1026 { 1027 rStrm >> maLineModel >> mnArrows >> mnStartPoint; 1028 rStrm.skip( 1 ); 1029 readMacroBiff4( rStrm, nMacroSize ); 1030 } 1031 1032 void BiffLineObject::implReadObjBiff5( BiffInputStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) 1033 { 1034 rStrm >> maLineModel >> mnArrows >> mnStartPoint; 1035 rStrm.skip( 1 ); 1036 readNameBiff5( rStrm, nNameLen ); 1037 readMacroBiff5( rStrm, nMacroSize ); 1038 } 1039 1040 Reference< XShape > BiffLineObject::implConvertAndInsert( BiffDrawingBase& rDrawing, 1041 const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const 1042 { 1043 ShapePropertyMap aPropMap( getBaseFilter().getModelObjectHelper() ); 1044 convertLineProperties( aPropMap, maLineModel, mnArrows ); 1045 1046 // create the line polygon 1047 PointSequenceSequence aPoints( 1 ); 1048 aPoints[ 0 ].realloc( 2 ); 1049 Point& rBeg = aPoints[ 0 ][ 0 ]; 1050 Point& rEnd = aPoints[ 0 ][ 1 ]; 1051 sal_Int32 nL = rShapeRect.X; 1052 sal_Int32 nT = rShapeRect.Y; 1053 sal_Int32 nR = rShapeRect.X + ::std::max< sal_Int32 >( rShapeRect.Width - 1, 0 ); 1054 sal_Int32 nB = rShapeRect.Y + ::std::max< sal_Int32 >( rShapeRect.Height - 1, 0 ); 1055 switch( mnStartPoint ) 1056 { 1057 default: 1058 case BIFF_OBJ_LINE_TL: rBeg.X = nL; rBeg.Y = nT; rEnd.X = nR; rEnd.Y = nB; break; 1059 case BIFF_OBJ_LINE_TR: rBeg.X = nR; rBeg.Y = nT; rEnd.X = nL; rEnd.Y = nB; break; 1060 case BIFF_OBJ_LINE_BR: rBeg.X = nR; rBeg.Y = nB; rEnd.X = nL; rEnd.Y = nT; break; 1061 case BIFF_OBJ_LINE_BL: rBeg.X = nL; rBeg.Y = nB; rEnd.X = nR; rEnd.Y = nT; break; 1062 } 1063 aPropMap.setProperty( PROP_PolyPolygon, aPoints ); 1064 aPropMap.setProperty( PROP_PolygonKind, PolygonKind_LINE ); 1065 1066 // create the shape 1067 Reference< XShape > xShape = rDrawing.createAndInsertXShape( CREATE_OUSTRING( "com.sun.star.drawing.LineShape" ), rxShapes, rShapeRect ); 1068 PropertySet( xShape ).setProperties( aPropMap ); 1069 return xShape; 1070 } 1071 1072 // ============================================================================ 1073 1074 BiffRectObject::BiffRectObject( const WorksheetHelper& rHelper ) : 1075 BiffDrawingObjectBase( rHelper ), 1076 mnFrameFlags( 0 ) 1077 { 1078 setAreaObj( true ); 1079 } 1080 1081 void BiffRectObject::readFrameData( BiffInputStream& rStrm ) 1082 { 1083 rStrm >> maFillModel >> maLineModel >> mnFrameFlags; 1084 } 1085 1086 void BiffRectObject::convertRectProperties( ShapePropertyMap& rPropMap ) const 1087 { 1088 convertLineProperties( rPropMap, maLineModel ); 1089 convertFillProperties( rPropMap, maFillModel ); 1090 convertFrameProperties( rPropMap, mnFrameFlags ); 1091 } 1092 1093 void BiffRectObject::implReadObjBiff3( BiffInputStream& rStrm, sal_uInt16 nMacroSize ) 1094 { 1095 readFrameData( rStrm ); 1096 readMacroBiff3( rStrm, nMacroSize ); 1097 } 1098 1099 void BiffRectObject::implReadObjBiff4( BiffInputStream& rStrm, sal_uInt16 nMacroSize ) 1100 { 1101 readFrameData( rStrm ); 1102 readMacroBiff4( rStrm, nMacroSize ); 1103 } 1104 1105 void BiffRectObject::implReadObjBiff5( BiffInputStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) 1106 { 1107 readFrameData( rStrm ); 1108 readNameBiff5( rStrm, nNameLen ); 1109 readMacroBiff5( rStrm, nMacroSize ); 1110 } 1111 1112 Reference< XShape > BiffRectObject::implConvertAndInsert( BiffDrawingBase& rDrawing, 1113 const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const 1114 { 1115 ShapePropertyMap aPropMap( getBaseFilter().getModelObjectHelper() ); 1116 convertRectProperties( aPropMap ); 1117 1118 Reference< XShape > xShape = rDrawing.createAndInsertXShape( CREATE_OUSTRING( "com.sun.star.drawing.RectangleShape" ), rxShapes, rShapeRect ); 1119 PropertySet( xShape ).setProperties( aPropMap ); 1120 return xShape; 1121 } 1122 1123 // ============================================================================ 1124 1125 BiffOvalObject::BiffOvalObject( const WorksheetHelper& rHelper ) : 1126 BiffRectObject( rHelper ) 1127 { 1128 } 1129 1130 Reference< XShape > BiffOvalObject::implConvertAndInsert( BiffDrawingBase& rDrawing, 1131 const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const 1132 { 1133 ShapePropertyMap aPropMap( getBaseFilter().getModelObjectHelper() ); 1134 convertRectProperties( aPropMap ); 1135 1136 Reference< XShape > xShape = rDrawing.createAndInsertXShape( CREATE_OUSTRING( "com.sun.star.drawing.EllipseShape" ), rxShapes, rShapeRect ); 1137 PropertySet( xShape ).setProperties( aPropMap ); 1138 return xShape; 1139 } 1140 1141 // ============================================================================ 1142 1143 BiffArcObject::BiffArcObject( const WorksheetHelper& rHelper ) : 1144 BiffDrawingObjectBase( rHelper ), 1145 mnQuadrant( BIFF_OBJ_ARC_TR ) 1146 { 1147 setAreaObj( false ); // arc may be 2-dimensional 1148 } 1149 1150 void BiffArcObject::implReadObjBiff3( BiffInputStream& rStrm, sal_uInt16 nMacroSize ) 1151 { 1152 rStrm >> maFillModel >> maLineModel >> mnQuadrant; 1153 rStrm.skip( 1 ); 1154 readMacroBiff3( rStrm, nMacroSize ); 1155 } 1156 1157 void BiffArcObject::implReadObjBiff4( BiffInputStream& rStrm, sal_uInt16 nMacroSize ) 1158 { 1159 rStrm >> maFillModel >> maLineModel >> mnQuadrant; 1160 rStrm.skip( 1 ); 1161 readMacroBiff4( rStrm, nMacroSize ); 1162 } 1163 1164 void BiffArcObject::implReadObjBiff5( BiffInputStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) 1165 { 1166 rStrm >> maFillModel >> maLineModel >> mnQuadrant; 1167 rStrm.skip( 1 ); 1168 readNameBiff5( rStrm, nNameLen ); 1169 readMacroBiff5( rStrm, nMacroSize ); 1170 } 1171 1172 Reference< XShape > BiffArcObject::implConvertAndInsert( BiffDrawingBase& rDrawing, 1173 const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const 1174 { 1175 ShapePropertyMap aPropMap( getBaseFilter().getModelObjectHelper() ); 1176 convertLineProperties( aPropMap, maLineModel ); 1177 convertFillProperties( aPropMap, maFillModel ); 1178 1179 /* Simulate arc objects with ellipse sections. While the original arc 1180 object uses the entire object rectangle, only one quarter of the 1181 ellipse shape will be visible. Thus, the size of the ellipse shape 1182 needs to be extended and its position adjusted according to the visible 1183 quadrant. */ 1184 Rectangle aNewRect( rShapeRect.X, rShapeRect.Y, rShapeRect.Width * 2, rShapeRect.Height * 2 ); 1185 long nStartAngle = 0; 1186 switch( mnQuadrant ) 1187 { 1188 default: 1189 case BIFF_OBJ_ARC_TR: nStartAngle = 0; aNewRect.X -= rShapeRect.Width; break; 1190 case BIFF_OBJ_ARC_TL: nStartAngle = 9000; break; 1191 case BIFF_OBJ_ARC_BL: nStartAngle = 18000; aNewRect.Y -= rShapeRect.Height; break; 1192 case BIFF_OBJ_ARC_BR: nStartAngle = 27000; aNewRect.X -= rShapeRect.Width; aNewRect.Y -= rShapeRect.Height; break; 1193 } 1194 long nEndAngle = (nStartAngle + 9000) % 36000; 1195 aPropMap.setProperty( PROP_CircleKind, maFillModel.isFilled() ? CircleKind_SECTION : CircleKind_ARC ); 1196 aPropMap.setProperty( PROP_CircleStartAngle, nStartAngle ); 1197 aPropMap.setProperty( PROP_CircleEndAngle, nEndAngle ); 1198 1199 // create the shape 1200 Reference< XShape > xShape = rDrawing.createAndInsertXShape( CREATE_OUSTRING( "com.sun.star.drawing.EllipseShape" ), rxShapes, aNewRect ); 1201 PropertySet( xShape ).setProperties( aPropMap ); 1202 return xShape; 1203 } 1204 1205 // ============================================================================ 1206 1207 BiffPolygonObject::BiffPolygonObject( const WorksheetHelper& rHelper ) : 1208 BiffRectObject( rHelper ), 1209 mnPolyFlags( 0 ), 1210 mnPointCount( 0 ) 1211 { 1212 setAreaObj( false ); // polygon may be 2-dimensional 1213 } 1214 1215 void BiffPolygonObject::implReadObjBiff4( BiffInputStream& rStrm, sal_uInt16 nMacroSize ) 1216 { 1217 readFrameData( rStrm ); 1218 rStrm >> mnPolyFlags; 1219 rStrm.skip( 10 ); 1220 rStrm >> mnPointCount; 1221 rStrm.skip( 8 ); 1222 readMacroBiff4( rStrm, nMacroSize ); 1223 importCoordList( rStrm ); 1224 } 1225 1226 void BiffPolygonObject::implReadObjBiff5( BiffInputStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) 1227 { 1228 readFrameData( rStrm ); 1229 rStrm >> mnPolyFlags; 1230 rStrm.skip( 10 ); 1231 rStrm >> mnPointCount; 1232 rStrm.skip( 8 ); 1233 readNameBiff5( rStrm, nNameLen ); 1234 readMacroBiff5( rStrm, nMacroSize ); 1235 importCoordList( rStrm ); 1236 } 1237 1238 namespace { 1239 1240 Point lclGetPolyPoint( const Rectangle& rAnchorRect, const Point& rPoint ) 1241 { 1242 // polygon coordinates are given in 1/16384 of shape size 1243 return Point( 1244 rAnchorRect.X + static_cast< sal_Int32 >( rAnchorRect.Width * getLimitedValue< double >( static_cast< double >( rPoint.X ) / 16384.0, 0.0, 1.0 ) + 0.5 ), 1245 rAnchorRect.Y + static_cast< sal_Int32 >( rAnchorRect.Height * getLimitedValue< double >( static_cast< double >( rPoint.Y ) / 16384.0, 0.0, 1.0 ) + 0.5 ) ); 1246 } 1247 1248 } // namespace 1249 1250 Reference< XShape > BiffPolygonObject::implConvertAndInsert( BiffDrawingBase& rDrawing, 1251 const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const 1252 { 1253 Reference< XShape > xShape; 1254 if( maCoords.size() >= 2 ) 1255 { 1256 ShapePropertyMap aPropMap( getBaseFilter().getModelObjectHelper() ); 1257 convertRectProperties( aPropMap ); 1258 1259 // create the polygon 1260 PointVector aPolygon; 1261 for( PointVector::const_iterator aIt = maCoords.begin(), aEnd = maCoords.end(); aIt != aEnd; ++aIt ) 1262 aPolygon.push_back( lclGetPolyPoint( rShapeRect, *aIt ) ); 1263 // close polygon if specified 1264 if( getFlag( mnPolyFlags, BIFF_OBJ_POLY_CLOSED ) && ((maCoords.front().X != maCoords.back().X) || (maCoords.front().Y != maCoords.back().Y)) ) 1265 aPolygon.push_back( aPolygon.front() ); 1266 PointSequenceSequence aPoints( 1 ); 1267 aPoints[ 0 ] = ContainerHelper::vectorToSequence( aPolygon ); 1268 aPropMap.setProperty( PROP_PolyPolygon, aPoints ); 1269 1270 // create the shape 1271 OUString aService = maFillModel.isFilled() ? 1272 CREATE_OUSTRING( "com.sun.star.drawing.PolyPolygonShape" ) : 1273 CREATE_OUSTRING( "com.sun.star.drawing.PolyLineShape" ); 1274 xShape = rDrawing.createAndInsertXShape( aService, rxShapes, rShapeRect ); 1275 PropertySet( xShape ).setProperties( aPropMap ); 1276 } 1277 return xShape; 1278 } 1279 1280 void BiffPolygonObject::importCoordList( BiffInputStream& rStrm ) 1281 { 1282 if( (rStrm.getNextRecId() == BIFF_ID_COORDLIST) && rStrm.startNextRecord() ) 1283 { 1284 OSL_ENSURE( rStrm.getRemaining() / 4 == mnPointCount, "BiffPolygonObject::importCoordList - wrong polygon point count" ); 1285 while( rStrm.getRemaining() >= 4 ) 1286 { 1287 sal_uInt16 nX, nY; 1288 rStrm >> nX >> nY; 1289 maCoords.push_back( Point( nX, nY ) ); 1290 } 1291 } 1292 } 1293 1294 // ============================================================================ 1295 // BIFF drawing page 1296 // ============================================================================ 1297 1298 BiffDrawingBase::BiffDrawingBase( const WorksheetHelper& rHelper, const Reference< XDrawPage >& rxDrawPage ) : 1299 WorksheetHelper( rHelper ), 1300 mxDrawPage( rxDrawPage ) 1301 { 1302 } 1303 1304 void BiffDrawingBase::importObj( BiffInputStream& rStrm ) 1305 { 1306 BiffDrawingObjectRef xDrawingObj; 1307 1308 #if 0 1309 /* #i61786# In BIFF8 streams, OBJ records may occur without MSODRAWING 1310 records. In this case, the OBJ records are in BIFF5 format. Do a sanity 1311 check here that there is no DFF data loaded before. */ 1312 DBG_ASSERT( maDffStrm.Tell() == 0, "BiffDrawingBase::importObj - unexpected DFF stream data, OBJ will be ignored" ); 1313 if( maDffStrm.Tell() == 0 ) switch( GetBiff() ) 1314 #else 1315 switch( getBiff() ) 1316 #endif 1317 { 1318 case BIFF3: 1319 xDrawingObj = BiffDrawingObjectBase::importObjBiff3( *this, rStrm ); 1320 break; 1321 case BIFF4: 1322 xDrawingObj = BiffDrawingObjectBase::importObjBiff4( *this, rStrm ); 1323 break; 1324 case BIFF5: 1325 // TODO: add BIFF8 when DFF is supported 1326 // case BIFF8: 1327 xDrawingObj = BiffDrawingObjectBase::importObjBiff5( *this, rStrm ); 1328 break; 1329 default:; 1330 } 1331 1332 if( xDrawingObj.get() ) 1333 { 1334 // insert into maRawObjs or into the last open group object 1335 maRawObjs.insertGrouped( xDrawingObj ); 1336 // to be able to find objects by ID 1337 maObjMapId[ xDrawingObj->getObjId() ] = xDrawingObj; 1338 } 1339 } 1340 1341 void BiffDrawingBase::setSkipObj( sal_uInt16 nObjId ) 1342 { 1343 /* Store identifiers of objects to be skipped in a separate list (the OBJ 1344 record may not be read yet). In the finalization phase, all objects 1345 registered here will be skipped. */ 1346 maSkipObjs.push_back( nObjId ); 1347 } 1348 1349 void BiffDrawingBase::finalizeImport() 1350 { 1351 Reference< XShapes > xShapes( mxDrawPage, UNO_QUERY ); 1352 OSL_ENSURE( xShapes.is(), "BiffDrawingBase::finalizeImport - no shapes container" ); 1353 if( !xShapes.is() ) 1354 return; 1355 1356 // process list of objects to be skipped 1357 for( BiffObjIdVector::const_iterator aIt = maSkipObjs.begin(), aEnd = maSkipObjs.end(); aIt != aEnd; ++aIt ) 1358 if( BiffDrawingObjectBase* pDrawingObj = maObjMapId.get( *aIt ).get() ) 1359 pDrawingObj->setProcessShape( false ); 1360 1361 // process drawing objects without DFF data 1362 maRawObjs.convertAndInsert( *this, xShapes ); 1363 } 1364 1365 Reference< XShape > BiffDrawingBase::createAndInsertXShape( const OUString& rService, 1366 const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const 1367 { 1368 OSL_ENSURE( rService.getLength() > 0, "BiffDrawingBase::createAndInsertXShape - missing UNO shape service name" ); 1369 OSL_ENSURE( rxShapes.is(), "BiffDrawingBase::createAndInsertXShape - missing XShapes container" ); 1370 Reference< XShape > xShape; 1371 if( (rService.getLength() > 0) && rxShapes.is() ) try 1372 { 1373 xShape.set( getBaseFilter().getModelFactory()->createInstance( rService ), UNO_QUERY_THROW ); 1374 // insert shape into passed shape collection (maybe drawpage or group shape) 1375 rxShapes->add( xShape ); 1376 xShape->setPosition( Point( rShapeRect.X, rShapeRect.Y ) ); 1377 xShape->setSize( Size( rShapeRect.Width, rShapeRect.Height ) ); 1378 } 1379 catch( Exception& ) 1380 { 1381 } 1382 OSL_ENSURE( xShape.is(), "BiffDrawingBase::createAndInsertXShape - cannot instanciate shape object" ); 1383 return xShape; 1384 } 1385 1386 // protected ------------------------------------------------------------------ 1387 1388 void BiffDrawingBase::appendRawObject( const BiffDrawingObjectRef& rxDrawingObj ) 1389 { 1390 OSL_ENSURE( rxDrawingObj.get(), "BiffDrawingBase::appendRawObject - unexpected empty object reference" ); 1391 maRawObjs.append( rxDrawingObj ); 1392 } 1393 1394 // ============================================================================ 1395 1396 BiffSheetDrawing::BiffSheetDrawing( const WorksheetHelper& rHelper ) : 1397 BiffDrawingBase( rHelper, rHelper.getDrawPage() ) 1398 { 1399 } 1400 1401 void BiffSheetDrawing::notifyShapeInserted( const Reference< XShape >& /*rxShape*/, const Rectangle& rShapeRect ) 1402 { 1403 // collect all shape positions in the WorksheetHelper base class 1404 extendShapeBoundingBox( rShapeRect ); 1405 } 1406 1407 // ============================================================================ 1408 1409 } // namespace xls 1410 } // namespace oox 1411