1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 #include "oox/xls/drawingbase.hxx" 29 30 #include <com/sun/star/awt/Rectangle.hpp> 31 #include "oox/helper/attributelist.hxx" 32 #include "oox/helper/binaryinputstream.hxx" 33 #include "oox/xls/unitconverter.hxx" 34 35 namespace oox { 36 namespace xls { 37 38 // ============================================================================ 39 40 using namespace ::com::sun::star::awt; 41 using namespace ::com::sun::star::table; 42 using namespace ::oox::drawingml; 43 44 using ::rtl::OUString; 45 46 // ============================================================================ 47 48 namespace { 49 50 /** Converts the passed 32-bit integer value from 1/100 mm to EMUs. */ 51 inline sal_Int64 lclHmmToEmu( sal_Int32 nValue ) 52 { 53 return (nValue < 0) ? -1 : convertHmmToEmu( nValue ); 54 } 55 56 /** Converts the passed 64-bit integer value from EMUs to 1/100 mm. */ 57 inline sal_Int32 lclEmuToHmm( sal_Int64 nValue ) 58 { 59 return (nValue < 0) ? -1 : convertEmuToHmm( nValue ); 60 } 61 62 /** Reads the cell anchor model from a BIFF or DFF stream. */ 63 BinaryInputStream& operator>>( BinaryInputStream& rStrm, CellAnchorModel& rModel ) 64 { 65 // all members are given as 16-bit unsigned values 66 rModel.mnCol = rStrm.readuInt16(); 67 rModel.mnColOffset = rStrm.readuInt16(); 68 rModel.mnRow = rStrm.readuInt16(); 69 rModel.mnRowOffset = rStrm.readuInt16(); 70 return rStrm; 71 } 72 73 } // namespace 74 75 // ============================================================================ 76 77 CellAnchorModel::CellAnchorModel() : 78 mnCol( -1 ), 79 mnRow( -1 ), 80 mnColOffset( 0 ), 81 mnRowOffset( 0 ) 82 { 83 } 84 85 // ---------------------------------------------------------------------------- 86 87 AnchorClientDataModel::AnchorClientDataModel() : 88 mbLocksWithSheet( true ), 89 mbPrintsWithSheet( true ) 90 { 91 } 92 93 // ============================================================================ 94 95 ShapeAnchor::ShapeAnchor( const WorksheetHelper& rHelper ) : 96 WorksheetHelper( rHelper ), 97 meAnchorType( ANCHOR_INVALID ), 98 meCellAnchorType( CELLANCHOR_EMU ), 99 mnEditAs( XML_twoCell ) 100 { 101 } 102 103 void ShapeAnchor::importAnchor( sal_Int32 nElement, const AttributeList& rAttribs ) 104 { 105 switch( nElement ) 106 { 107 case XDR_TOKEN( absoluteAnchor ): 108 meAnchorType = ANCHOR_ABSOLUTE; 109 break; 110 case XDR_TOKEN( oneCellAnchor ): 111 meAnchorType = ANCHOR_ONECELL; 112 break; 113 case XDR_TOKEN( twoCellAnchor ): 114 meAnchorType = ANCHOR_TWOCELL; 115 mnEditAs = rAttribs.getToken( XML_editAs, XML_twoCell ); 116 break; 117 default: 118 OSL_ENSURE( false, "ShapeAnchor::importAnchor - unexpected element" ); 119 } 120 meCellAnchorType = CELLANCHOR_EMU; 121 } 122 123 void ShapeAnchor::importPos( const AttributeList& rAttribs ) 124 { 125 OSL_ENSURE( meAnchorType == ANCHOR_ABSOLUTE, "ShapeAnchor::importPos - unexpected 'xdr:pos' element" ); 126 maPos.X = rAttribs.getHyper( XML_x, 0 ); 127 maPos.Y = rAttribs.getHyper( XML_y, 0 ); 128 } 129 130 void ShapeAnchor::importExt( const AttributeList& rAttribs ) 131 { 132 OSL_ENSURE( (meAnchorType == ANCHOR_ABSOLUTE) || (meAnchorType == ANCHOR_ONECELL), "ShapeAnchor::importExt - unexpected 'xdr:ext' element" ); 133 maSize.Width = rAttribs.getHyper( XML_cx, 0 ); 134 maSize.Height = rAttribs.getHyper( XML_cy, 0 ); 135 } 136 137 void ShapeAnchor::importClientData( const AttributeList& rAttribs ) 138 { 139 maClientData.mbLocksWithSheet = rAttribs.getBool( XML_fLocksWithSheet, true ); 140 maClientData.mbPrintsWithSheet = rAttribs.getBool( XML_fPrintsWithSheet, true ); 141 } 142 143 void ShapeAnchor::setCellPos( sal_Int32 nElement, sal_Int32 nParentContext, const OUString& rValue ) 144 { 145 CellAnchorModel* pCellAnchor = 0; 146 switch( nParentContext ) 147 { 148 case XDR_TOKEN( from ): 149 OSL_ENSURE( (meAnchorType == ANCHOR_ONECELL) || (meAnchorType == ANCHOR_TWOCELL), "ShapeAnchor::setCellPos - unexpected 'xdr:from' element" ); 150 pCellAnchor = &maFrom; 151 break; 152 case XDR_TOKEN( to ): 153 OSL_ENSURE( meAnchorType == ANCHOR_TWOCELL, "ShapeAnchor::setCellPos - unexpected 'xdr:to' element" ); 154 pCellAnchor = &maTo; 155 break; 156 default: 157 OSL_ENSURE( false, "ShapeAnchor::setCellPos - unexpected parent element" ); 158 } 159 if( pCellAnchor ) switch( nElement ) 160 { 161 case XDR_TOKEN( col ): pCellAnchor->mnCol = rValue.toInt32(); break; 162 case XDR_TOKEN( row ): pCellAnchor->mnRow = rValue.toInt32(); break; 163 case XDR_TOKEN( colOff ): pCellAnchor->mnColOffset = rValue.toInt64(); break; 164 case XDR_TOKEN( rowOff ): pCellAnchor->mnRowOffset = rValue.toInt64(); break; 165 default: OSL_ENSURE( false, "ShapeAnchor::setCellPos - unexpected element" ); 166 } 167 } 168 169 void ShapeAnchor::importVmlAnchor( const OUString& rAnchor ) 170 { 171 meAnchorType = ANCHOR_TWOCELL; /// VML uses two-cell anchors only 172 meCellAnchorType = CELLANCHOR_PIXEL; /// VML uses screen pixels for offset values 173 174 ::std::vector< OUString > aTokens; 175 sal_Int32 nIndex = 0; 176 while( nIndex >= 0 ) 177 aTokens.push_back( rAnchor.getToken( 0, ',', nIndex ).trim() ); 178 179 OSL_ENSURE( aTokens.size() >= 8, "ShapeAnchor::importVmlAnchor - missing anchor tokens" ); 180 if( aTokens.size() >= 8 ) 181 { 182 maFrom.mnCol = aTokens[ 0 ].toInt32(); 183 maFrom.mnColOffset = aTokens[ 1 ].toInt32(); 184 maFrom.mnRow = aTokens[ 2 ].toInt32(); 185 maFrom.mnRowOffset = aTokens[ 3 ].toInt32(); 186 maTo.mnCol = aTokens[ 4 ].toInt32(); 187 maTo.mnColOffset = aTokens[ 5 ].toInt32(); 188 maTo.mnRow = aTokens[ 6 ].toInt32(); 189 maTo.mnRowOffset = aTokens[ 7 ].toInt32(); 190 } 191 } 192 193 void ShapeAnchor::importBiffAnchor( BinaryInputStream& rStrm ) 194 { 195 meAnchorType = ANCHOR_TWOCELL; /// BIFF/DFF use two-cell anchors only 196 meCellAnchorType = CELLANCHOR_COLROW; /// BIFF/DFF use fraction of column/row for offset values 197 rStrm >> maFrom >> maTo; 198 } 199 200 EmuRectangle ShapeAnchor::calcAnchorRectEmu( const Size& rPageSizeHmm ) const 201 { 202 AddressConverter& rAddrConv = getAddressConverter(); 203 EmuSize aPageSize( lclHmmToEmu( rPageSizeHmm.Width ), lclHmmToEmu( rPageSizeHmm.Height ) ); 204 EmuRectangle aAnchorRect( -1, -1, -1, -1 ); 205 206 // calculate shape position 207 switch( meAnchorType ) 208 { 209 case ANCHOR_ABSOLUTE: 210 OSL_ENSURE( maPos.isValid(), "ShapeAnchor::calcAnchorRectEmu - invalid position" ); 211 if( maPos.isValid() && (maPos.X < aPageSize.Width) && (maPos.Y < aPageSize.Height) ) 212 aAnchorRect.setPos( maPos ); 213 break; 214 case ANCHOR_ONECELL: 215 case ANCHOR_TWOCELL: 216 OSL_ENSURE( maFrom.isValid(), "ShapeAnchor::calcAnchorRectEmu - invalid position" ); 217 if( maFrom.isValid() && rAddrConv.checkCol( maFrom.mnCol, true ) && rAddrConv.checkRow( maFrom.mnRow, true ) ) 218 { 219 EmuPoint aPoint = calcCellAnchorEmu( maFrom ); 220 if( (aPoint.X < aPageSize.Width) && (aPoint.Y < aPageSize.Height) ) 221 aAnchorRect.setPos( aPoint ); 222 } 223 break; 224 case ANCHOR_INVALID: 225 OSL_ENSURE( false, "ShapeAnchor::calcAnchorRectEmu - invalid anchor" ); 226 break; 227 } 228 229 // calculate shape size 230 if( (aAnchorRect.X >= 0) && (aAnchorRect.Y >= 0) ) switch( meAnchorType ) 231 { 232 case ANCHOR_ABSOLUTE: 233 case ANCHOR_ONECELL: 234 OSL_ENSURE( maSize.isValid(), "ShapeAnchor::calcAnchorRectEmu - invalid size" ); 235 if( maSize.isValid() ) 236 { 237 aAnchorRect.Width = ::std::min< sal_Int64 >( maSize.Width, aPageSize.Width - aAnchorRect.X ); 238 aAnchorRect.Height = ::std::min< sal_Int64 >( maSize.Height, aPageSize.Height - aAnchorRect.Y ); 239 } 240 break; 241 case ANCHOR_TWOCELL: 242 OSL_ENSURE( maTo.isValid(), "ShapeAnchor::calcAnchorRectEmu - invalid position" ); 243 if( maTo.isValid() ) 244 { 245 /* Pass a valid cell address to calcCellAnchorEmu(), otherwise 246 nothing useful is returned, even if either row or column is valid. */ 247 CellAddress aToCell = rAddrConv.createValidCellAddress( BinAddress( maTo.mnCol, maTo.mnRow ), getSheetIndex(), true ); 248 CellAnchorModel aValidTo = maTo; 249 aValidTo.mnCol = aToCell.Column; 250 aValidTo.mnRow = aToCell.Row; 251 EmuPoint aPoint = calcCellAnchorEmu( aValidTo ); 252 // width (if column index is valid, use the calculated offset, otherwise stretch to maximum available X position) 253 aAnchorRect.Width = aPageSize.Width - aAnchorRect.X; 254 if( aToCell.Column == maTo.mnCol ) 255 aAnchorRect.Width = ::std::min< sal_Int64 >( aPoint.X - aAnchorRect.X + 1, aAnchorRect.Width ); 256 // height (if row index is valid, use the calculated offset, otherwise stretch to maximum available Y position) 257 aAnchorRect.Height = aPageSize.Height - aAnchorRect.Y; 258 if( aToCell.Row == maTo.mnRow ) 259 aAnchorRect.Height = ::std::min< sal_Int64 >( aPoint.Y - aAnchorRect.Y + 1, aAnchorRect.Height ); 260 } 261 break; 262 case ANCHOR_INVALID: 263 break; 264 } 265 266 // add 0.75 mm (27,000 EMUs) in X direction to correct display error 267 if( aAnchorRect.X >= 0 ) 268 aAnchorRect.X += 27000; 269 // remove 0.25 mm (9,000 EMUs) in Y direction to correct display error 270 if( aAnchorRect.Y >= 9000 ) 271 aAnchorRect.Y -= 9000; 272 273 return aAnchorRect; 274 } 275 276 Rectangle ShapeAnchor::calcAnchorRectHmm( const Size& rPageSizeHmm ) const 277 { 278 EmuRectangle aAnchorRect = calcAnchorRectEmu( rPageSizeHmm ); 279 return Rectangle( lclEmuToHmm( aAnchorRect.X ), lclEmuToHmm( aAnchorRect.Y ), lclEmuToHmm( aAnchorRect.Width ), lclEmuToHmm( aAnchorRect.Height ) ); 280 } 281 282 // private -------------------------------------------------------------------- 283 284 EmuPoint ShapeAnchor::calcCellAnchorEmu( const CellAnchorModel& rModel ) const 285 { 286 // calculate position of top-left edge of the cell 287 Point aPoint = getCellPosition( rModel.mnCol, rModel.mnRow ); 288 EmuPoint aEmuPoint( lclHmmToEmu( aPoint.X ), lclHmmToEmu( aPoint.Y ) ); 289 290 // add the offset inside the cell 291 switch( meCellAnchorType ) 292 { 293 case CELLANCHOR_EMU: 294 aEmuPoint.X += rModel.mnColOffset; 295 aEmuPoint.Y += rModel.mnRowOffset; 296 break; 297 298 case CELLANCHOR_PIXEL: 299 { 300 const UnitConverter& rUnitConv = getUnitConverter(); 301 aEmuPoint.X += static_cast< sal_Int64 >( rUnitConv.scaleValue( static_cast< double >( rModel.mnColOffset ), UNIT_SCREENX, UNIT_EMU ) ); 302 aEmuPoint.Y += static_cast< sal_Int64 >( rUnitConv.scaleValue( static_cast< double >( rModel.mnRowOffset ), UNIT_SCREENY, UNIT_EMU ) ); 303 } 304 break; 305 306 case CELLANCHOR_COLROW: 307 { 308 Size aCellSize = getCellSize( rModel.mnCol, rModel.mnRow ); 309 EmuSize aEmuSize( lclHmmToEmu( aCellSize.Width ), lclHmmToEmu( aCellSize.Height ) ); 310 // X offset is given in 1/1024 of column width 311 aEmuPoint.X += static_cast< sal_Int64 >( aEmuSize.Width * getLimitedValue< double >( static_cast< double >( rModel.mnColOffset ) / 1024.0, 0.0, 1.0 ) + 0.5 ); 312 // Y offset is given in 1/256 of row height 313 aEmuPoint.Y += static_cast< sal_Int64 >( aEmuSize.Height * getLimitedValue< double >( static_cast< double >( rModel.mnRowOffset ) / 256.0, 0.0, 1.0 ) + 0.5 ); 314 } 315 break; 316 } 317 318 return aEmuPoint; 319 } 320 321 // ============================================================================ 322 323 } // namespace xls 324 } // namespace oox 325