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