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/sheetdatacontext.hxx"
25 
26 #include <com/sun/star/table/CellContentType.hpp>
27 #include <com/sun/star/table/XCell.hpp>
28 #include <com/sun/star/table/XCellRange.hpp>
29 #include <com/sun/star/text/XText.hpp>
30 #include "oox/helper/attributelist.hxx"
31 #include "oox/helper/propertyset.hxx"
32 #include "oox/xls/addressconverter.hxx"
33 #include "oox/xls/biffinputstream.hxx"
34 #include "oox/xls/formulaparser.hxx"
35 #include "oox/xls/richstringcontext.hxx"
36 #include "oox/xls/unitconverter.hxx"
37 
38 namespace oox {
39 namespace xls {
40 
41 // ============================================================================
42 
43 using namespace ::com::sun::star::sheet;
44 using namespace ::com::sun::star::table;
45 using namespace ::com::sun::star::text;
46 using namespace ::com::sun::star::uno;
47 
48 using ::oox::core::ContextHandlerRef;
49 using ::rtl::OUString;
50 
51 // ============================================================================
52 
53 namespace {
54 
55 // record constants -----------------------------------------------------------
56 
57 const sal_uInt32 BIFF12_CELL_SHOWPHONETIC   = 0x01000000;
58 
59 const sal_uInt8 BIFF12_DATATABLE_ROW        = 0x01;
60 const sal_uInt8 BIFF12_DATATABLE_2D         = 0x02;
61 const sal_uInt8 BIFF12_DATATABLE_REF1DEL    = 0x04;
62 const sal_uInt8 BIFF12_DATATABLE_REF2DEL    = 0x08;
63 
64 const sal_uInt16 BIFF12_ROW_THICKTOP        = 0x0001;
65 const sal_uInt16 BIFF12_ROW_THICKBOTTOM     = 0x0002;
66 const sal_uInt16 BIFF12_ROW_COLLAPSED       = 0x0800;
67 const sal_uInt16 BIFF12_ROW_HIDDEN          = 0x1000;
68 const sal_uInt16 BIFF12_ROW_CUSTOMHEIGHT    = 0x2000;
69 const sal_uInt16 BIFF12_ROW_CUSTOMFORMAT    = 0x4000;
70 const sal_uInt8 BIFF12_ROW_SHOWPHONETIC     = 0x01;
71 
72 const sal_uInt16 BIFF_DATATABLE_ROW         = 0x0004;
73 const sal_uInt16 BIFF_DATATABLE_2D          = 0x0008;
74 const sal_uInt16 BIFF_DATATABLE_REF1DEL     = 0x0010;
75 const sal_uInt16 BIFF_DATATABLE_REF2DEL     = 0x0020;
76 
77 const sal_uInt8 BIFF_FORMULA_RES_STRING     = 0;        /// Result is a string.
78 const sal_uInt8 BIFF_FORMULA_RES_BOOL       = 1;        /// Result is Boolean value.
79 const sal_uInt8 BIFF_FORMULA_RES_ERROR      = 2;        /// Result is error code.
80 const sal_uInt8 BIFF_FORMULA_RES_EMPTY      = 3;        /// Result is empty cell (BIFF8 only).
81 const sal_uInt16 BIFF_FORMULA_SHARED        = 0x0008;   /// Shared formula cell.
82 
83 const sal_uInt8 BIFF2_ROW_CUSTOMFORMAT      = 0x01;
84 const sal_uInt16 BIFF_ROW_DEFAULTHEIGHT     = 0x8000;
85 const sal_uInt16 BIFF_ROW_HEIGHTMASK        = 0x7FFF;
86 const sal_uInt32 BIFF_ROW_COLLAPSED         = 0x00000010;
87 const sal_uInt32 BIFF_ROW_HIDDEN            = 0x00000020;
88 const sal_uInt32 BIFF_ROW_CUSTOMHEIGHT      = 0x00000040;
89 const sal_uInt32 BIFF_ROW_CUSTOMFORMAT      = 0x00000080;
90 const sal_uInt32 BIFF_ROW_THICKTOP          = 0x10000000;
91 const sal_uInt32 BIFF_ROW_THICKBOTTOM       = 0x20000000;
92 const sal_uInt32 BIFF_ROW_SHOWPHONETIC      = 0x40000000;
93 
94 const sal_Int32 BIFF2_CELL_USEIXFE          = 63;
95 
96 } // namespace
97 
98 // ============================================================================
99 
100 SheetDataContextBase::SheetDataContextBase( const WorksheetHelper& rHelper ) :
101     mrAddressConv( rHelper.getAddressConverter() ),
102     mrFormulaParser( rHelper.getFormulaParser() ),
103     mrSheetData( rHelper.getSheetData() ),
104     mnSheet( rHelper.getSheetIndex() )
105 {
106     maLastCellAddress.Sheet = rHelper.getSheetIndex();
107     maLastCellAddress.Row = SAL_MAX_UINT32; // wraps around to 0 when incremented
108 }
109 
110 SheetDataContextBase::~SheetDataContextBase()
111 {
112 }
113 
114 // ============================================================================
115 
116 SheetDataContext::SheetDataContext( WorksheetFragmentBase& rFragment ) :
117     WorksheetContextBase( rFragment ),
118     SheetDataContextBase( rFragment ),
119     mbHasFormula( false ),
120     mbValidRange( false )
121 {
122 }
123 
124 ContextHandlerRef SheetDataContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
125 {
126     switch( getCurrentElement() )
127     {
128         case XLS_TOKEN( sheetData ):
129             if( nElement == XLS_TOKEN( row ) ) { importRow( rAttribs ); return this; }
130         break;
131 
132         case XLS_TOKEN( row ):
133             // do not process cell elements with invalid (out-of-range) address
134             if( nElement == XLS_TOKEN( c ) && importCell( rAttribs ) )
135                 return this;
136         break;
137 
138         case XLS_TOKEN( c ):
139             switch( nElement )
140             {
141                 case XLS_TOKEN( is ):
142                     mxInlineStr.reset( new RichString( *this ) );
143                     return new RichStringContext( *this, mxInlineStr );
144                 case XLS_TOKEN( v ):
145                     return this;    // characters contain cell value
146                 case XLS_TOKEN( f ):
147                     importFormula( rAttribs );
148                     return this;    // characters contain formula string
149             }
150         break;
151     }
152     return 0;
153 }
154 
155 void SheetDataContext::onCharacters( const OUString& rChars )
156 {
157     switch( getCurrentElement() )
158     {
159         case XLS_TOKEN( v ):
160             maCellValue = rChars;
161         break;
162         case XLS_TOKEN( f ):
163             if( maFmlaData.mnFormulaType != XML_TOKEN_INVALID )
164                 maTokens = mrFormulaParser.importFormula( maCellData.maCellAddr, rChars );
165         break;
166     }
167 }
168 
169 void SheetDataContext::onEndElement()
170 {
171     if( getCurrentElement() == XLS_TOKEN( c ) )
172     {
173         // try to create a formula cell
174         if( mbHasFormula ) switch( maFmlaData.mnFormulaType )
175         {
176             case XML_normal:
177                 mrSheetData.setFormulaCell( maCellData, maTokens );
178             break;
179             case XML_shared:
180                 if( maFmlaData.mnSharedId >= 0 )
181                 {
182                     if( mbValidRange && maFmlaData.isValidSharedRef( maCellData.maCellAddr ) )
183                         mrSheetData.createSharedFormula( maFmlaData.mnSharedId, maTokens );
184                     mrSheetData.setFormulaCell( maCellData, maFmlaData.mnSharedId );
185                 }
186                 else
187                     // no success, set plain cell value and formatting below
188                     mbHasFormula = false;
189             break;
190             case XML_array:
191                 if( mbValidRange && maFmlaData.isValidArrayRef( maCellData.maCellAddr ) )
192                     mrSheetData.createArrayFormula( maFmlaData.maFormulaRef, maTokens );
193                 // set cell formatting, but do not set result as cell value
194                 mrSheetData.setBlankCell( maCellData );
195             break;
196             case XML_dataTable:
197                 if( mbValidRange )
198                     mrSheetData.createTableOperation( maFmlaData.maFormulaRef, maTableData );
199                 // set cell formatting, but do not set result as cell value
200                 mrSheetData.setBlankCell( maCellData );
201             break;
202             default:
203                 OSL_ENSURE( maFmlaData.mnFormulaType == XML_TOKEN_INVALID, "SheetDataContext::onEndElement - unknown formula type" );
204                 mbHasFormula = false;
205         }
206 
207         if( !mbHasFormula )
208         {
209             // no formula created: try to set the cell value
210             if( maCellValue.getLength() > 0 ) switch( maCellData.mnCellType )
211             {
212                 case XML_n:
213                     mrSheetData.setValueCell( maCellData, maCellValue.toDouble() );
214                 break;
215                 case XML_b:
216                     mrSheetData.setBooleanCell( maCellData, maCellValue.toDouble() != 0.0 );
217                 break;
218                 case XML_e:
219                     mrSheetData.setErrorCell( maCellData, maCellValue );
220                 break;
221                 case XML_str:
222                     mrSheetData.setStringCell( maCellData, maCellValue );
223                 break;
224                 case XML_s:
225                     mrSheetData.setStringCell( maCellData, maCellValue.toInt32() );
226                 break;
227             }
228             else if( (maCellData.mnCellType == XML_inlineStr) && mxInlineStr.get() )
229             {
230                 mxInlineStr->finalizeImport();
231                 mrSheetData.setStringCell( maCellData, mxInlineStr );
232             }
233             else
234             {
235                 // empty cell, update cell type
236                 maCellData.mnCellType = XML_TOKEN_INVALID;
237                 mrSheetData.setBlankCell( maCellData );
238             }
239         }
240     }
241 }
242 
243 ContextHandlerRef SheetDataContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
244 {
245     switch( getCurrentElement() )
246     {
247         case BIFF12_ID_SHEETDATA:
248             if( nRecId == BIFF12_ID_ROW ) { importRow( rStrm ); return this; }
249         break;
250 
251         case BIFF12_ID_ROW:
252             switch( nRecId )
253             {
254                 case BIFF12_ID_ARRAY:           importArray( rStrm );                           break;
255                 case BIFF12_ID_CELL_BOOL:       importCellBool( rStrm, CELLTYPE_VALUE );        break;
256                 case BIFF12_ID_CELL_BLANK:      importCellBlank( rStrm, CELLTYPE_VALUE );       break;
257                 case BIFF12_ID_CELL_DOUBLE:     importCellDouble( rStrm, CELLTYPE_VALUE );      break;
258                 case BIFF12_ID_CELL_ERROR:      importCellError( rStrm, CELLTYPE_VALUE );       break;
259                 case BIFF12_ID_CELL_RK:         importCellRk( rStrm, CELLTYPE_VALUE );          break;
260                 case BIFF12_ID_CELL_RSTRING:    importCellRString( rStrm, CELLTYPE_VALUE );     break;
261                 case BIFF12_ID_CELL_SI:         importCellSi( rStrm, CELLTYPE_VALUE );          break;
262                 case BIFF12_ID_CELL_STRING:     importCellString( rStrm, CELLTYPE_VALUE );      break;
263                 case BIFF12_ID_DATATABLE:       importDataTable( rStrm );                       break;
264                 case BIFF12_ID_FORMULA_BOOL:    importCellBool( rStrm, CELLTYPE_FORMULA );      break;
265                 case BIFF12_ID_FORMULA_DOUBLE:  importCellDouble( rStrm, CELLTYPE_FORMULA );    break;
266                 case BIFF12_ID_FORMULA_ERROR:   importCellError( rStrm, CELLTYPE_FORMULA );     break;
267                 case BIFF12_ID_FORMULA_STRING:  importCellString( rStrm, CELLTYPE_FORMULA );    break;
268                 case BIFF12_ID_MULTCELL_BOOL:   importCellBool( rStrm, CELLTYPE_MULTI );        break;
269                 case BIFF12_ID_MULTCELL_BLANK:  importCellBlank( rStrm, CELLTYPE_MULTI );       break;
270                 case BIFF12_ID_MULTCELL_DOUBLE: importCellDouble( rStrm, CELLTYPE_MULTI );      break;
271                 case BIFF12_ID_MULTCELL_ERROR:  importCellError( rStrm, CELLTYPE_MULTI );       break;
272                 case BIFF12_ID_MULTCELL_RK:     importCellRk( rStrm, CELLTYPE_MULTI );          break;
273                 case BIFF12_ID_MULTCELL_RSTRING:importCellRString( rStrm, CELLTYPE_MULTI );     break;
274                 case BIFF12_ID_MULTCELL_SI:     importCellSi( rStrm, CELLTYPE_MULTI );          break;
275                 case BIFF12_ID_MULTCELL_STRING: importCellString( rStrm, CELLTYPE_MULTI );      break;
276                 case BIFF12_ID_SHAREDFMLA:      importSharedFmla( rStrm );                      break;
277             }
278         break;
279     }
280     return 0;
281 }
282 
283 // private --------------------------------------------------------------------
284 
285 void SheetDataContext::importRow( const AttributeList& rAttribs )
286 {
287     RowModel aModel;
288     aModel.mnRow          = rAttribs.getInteger( XML_r, -1 );
289     if ( aModel.mnRow == -1 )
290         aModel.mnRow = ++maLastCellAddress.Row;
291     else
292         maLastCellAddress.Row = aModel.mnRow - 1;
293     maLastCellAddress.Column = SAL_MAX_UINT32; // wraps around to 0 when incremented
294     aModel.mfHeight       = rAttribs.getDouble( XML_ht, -1.0 );
295     aModel.mnXfId         = rAttribs.getInteger( XML_s, -1 );
296     aModel.mnLevel        = rAttribs.getInteger( XML_outlineLevel, 0 );
297     aModel.mbCustomHeight = rAttribs.getBool( XML_customHeight, false );
298     aModel.mbCustomFormat = rAttribs.getBool( XML_customFormat, false );
299     aModel.mbShowPhonetic = rAttribs.getBool( XML_ph, false );
300     aModel.mbHidden       = rAttribs.getBool( XML_hidden, false );
301     aModel.mbCollapsed    = rAttribs.getBool( XML_collapsed, false );
302     aModel.mbThickTop     = rAttribs.getBool( XML_thickTop, false );
303     aModel.mbThickBottom  = rAttribs.getBool( XML_thickBot, false );
304 
305     // decode the column spans (space-separated list of colon-separated integer pairs)
306     OUString aColSpansText = rAttribs.getString( XML_spans, OUString() );
307     sal_Int32 nMaxCol = mrAddressConv.getMaxApiAddress().Column;
308     sal_Int32 nIndex = 0;
309     while( nIndex >= 0 )
310     {
311         OUString aColSpanToken = aColSpansText.getToken( 0, ' ', nIndex );
312         sal_Int32 nSepPos = aColSpanToken.indexOf( ':' );
313         if( (0 < nSepPos) && (nSepPos + 1 < aColSpanToken.getLength()) )
314         {
315             // OOXML uses 1-based integer column indexes, row model expects 0-based colspans
316             sal_Int32 nLastCol = ::std::min( aColSpanToken.copy( nSepPos + 1 ).toInt32() - 1, nMaxCol );
317             aModel.insertColSpan( ValueRange( aColSpanToken.copy( 0, nSepPos ).toInt32() - 1, nLastCol ) );
318         }
319     }
320 
321     // set row properties in the current sheet
322     setRowModel( aModel );
323 }
324 
325 bool SheetDataContext::importCell( const AttributeList& rAttribs )
326 {
327     OUString r = rAttribs.getString( XML_r, OUString() );
328     bool bValidAddr;
329     if ( r.getLength() > 0 )
330         bValidAddr = mrAddressConv.convertToCellAddress( maCellData.maCellAddr, rAttribs.getString( XML_r, OUString() ), mnSheet, true );
331     else
332     {
333         maCellData.maCellAddr.Column = ++maLastCellAddress.Column;
334         maCellData.maCellAddr.Row = maLastCellAddress.Row;
335         maCellData.maCellAddr.Sheet = maLastCellAddress.Sheet;
336         bValidAddr = true;
337     }
338     if( bValidAddr )
339     {
340         maLastCellAddress.Column  = maCellData.maCellAddr.Column;
341         maLastCellAddress.Row     = maCellData.maCellAddr.Row;
342         maLastCellAddress.Sheet   = maCellData.maCellAddr.Sheet;
343         maCellData.mnCellType     = rAttribs.getToken( XML_t, XML_n );
344         maCellData.mnXfId         = rAttribs.getInteger( XML_s, -1 );
345         maCellData.mbShowPhonetic = rAttribs.getBool( XML_ph, false );
346 
347         // reset cell value, formula settings, and inline string
348         maCellValue = OUString();
349         mxInlineStr.reset();
350         mbHasFormula = false;
351 
352         // update used area of the sheet
353         extendUsedArea( maCellData.maCellAddr );
354     }
355     return bValidAddr;
356 }
357 
358 void SheetDataContext::importFormula( const AttributeList& rAttribs )
359 {
360     mbHasFormula = true;
361     mbValidRange = mrAddressConv.convertToCellRange( maFmlaData.maFormulaRef, rAttribs.getString( XML_ref, OUString() ), mnSheet, true, true );
362 
363     maFmlaData.mnFormulaType = rAttribs.getToken( XML_t, XML_normal );
364     maFmlaData.mnSharedId    = rAttribs.getInteger( XML_si, -1 );
365 
366     if( maFmlaData.mnFormulaType == XML_dataTable )
367     {
368         maTableData.maRef1        = rAttribs.getString( XML_r1, OUString() );
369         maTableData.maRef2        = rAttribs.getString( XML_r2, OUString() );
370         maTableData.mb2dTable     = rAttribs.getBool( XML_dt2D, false );
371         maTableData.mbRowTable    = rAttribs.getBool( XML_dtr, false );
372         maTableData.mbRef1Deleted = rAttribs.getBool( XML_del1, false );
373         maTableData.mbRef2Deleted = rAttribs.getBool( XML_del2, false );
374     }
375 
376     // clear token array, will be regenerated from element text
377     maTokens = ApiTokenSequence();
378 }
379 
380 void SheetDataContext::importRow( SequenceInputStream& rStrm )
381 {
382     RowModel aModel;
383     sal_Int32 nSpanCount;
384     sal_uInt16 nHeight, nFlags1;
385     sal_uInt8 nFlags2;
386     rStrm >> maCurrPos.mnRow >> aModel.mnXfId >> nHeight >> nFlags1 >> nFlags2 >> nSpanCount;
387     maCurrPos.mnCol = 0;
388 
389     // row index is 0-based in BIFF12, but RowModel expects 1-based
390     aModel.mnRow          = maCurrPos.mnRow + 1;
391     // row height is in twips in BIFF12, convert to points
392     aModel.mfHeight       = nHeight / 20.0;
393     aModel.mnLevel        = extractValue< sal_Int32 >( nFlags1, 8, 3 );
394     aModel.mbCustomHeight = getFlag( nFlags1, BIFF12_ROW_CUSTOMHEIGHT );
395     aModel.mbCustomFormat = getFlag( nFlags1, BIFF12_ROW_CUSTOMFORMAT );
396     aModel.mbShowPhonetic = getFlag( nFlags2, BIFF12_ROW_SHOWPHONETIC );
397     aModel.mbHidden       = getFlag( nFlags1, BIFF12_ROW_HIDDEN );
398     aModel.mbCollapsed    = getFlag( nFlags1, BIFF12_ROW_COLLAPSED );
399     aModel.mbThickTop     = getFlag( nFlags1, BIFF12_ROW_THICKTOP );
400     aModel.mbThickBottom  = getFlag( nFlags1, BIFF12_ROW_THICKBOTTOM );
401 
402     // read the column spans
403     sal_Int32 nMaxCol = mrAddressConv.getMaxApiAddress().Column;
404     for( sal_Int32 nSpanIdx = 0; (nSpanIdx < nSpanCount) && !rStrm.isEof(); ++nSpanIdx )
405     {
406         sal_Int32 nFirstCol, nLastCol;
407         rStrm >> nFirstCol >> nLastCol;
408         aModel.insertColSpan( ValueRange( nFirstCol, ::std::min( nLastCol, nMaxCol ) ) );
409     }
410 
411     // set row properties in the current sheet
412     setRowModel( aModel );
413 }
414 
415 bool SheetDataContext::readCellHeader( SequenceInputStream& rStrm, CellType eCellType )
416 {
417     switch( eCellType )
418     {
419         case CELLTYPE_VALUE:
420         case CELLTYPE_FORMULA:  rStrm >> maCurrPos.mnCol;   break;
421         case CELLTYPE_MULTI:    ++maCurrPos.mnCol;          break;
422     }
423 
424     sal_uInt32 nXfId;
425     rStrm >> nXfId;
426 
427     bool bValidAddr = mrAddressConv.convertToCellAddress( maCellData.maCellAddr, maCurrPos, mnSheet, true );
428     maCellData.mnXfId = extractValue< sal_Int32 >( nXfId, 0, 24 );
429     maCellData.mbShowPhonetic = getFlag( nXfId, BIFF12_CELL_SHOWPHONETIC );
430 
431     // update used area of the sheet
432     if( bValidAddr )
433         extendUsedArea( maCellData.maCellAddr );
434     return bValidAddr;
435 }
436 
437 ApiTokenSequence SheetDataContext::readCellFormula( SequenceInputStream& rStrm )
438 {
439     rStrm.skip( 2 );
440     return mrFormulaParser.importFormula( maCellData.maCellAddr, FORMULATYPE_CELL, rStrm );
441 }
442 
443 bool SheetDataContext::readFormulaRef( SequenceInputStream& rStrm )
444 {
445     BinRange aRange;
446     rStrm >> aRange;
447     return mrAddressConv.convertToCellRange( maFmlaData.maFormulaRef, aRange, mnSheet, true, true );
448 }
449 
450 void SheetDataContext::importCellBool( SequenceInputStream& rStrm, CellType eCellType )
451 {
452     if( readCellHeader( rStrm, eCellType ) )
453     {
454         maCellData.mnCellType = XML_b;
455         bool bValue = rStrm.readuInt8() != 0;
456         if( eCellType == CELLTYPE_FORMULA )
457             mrSheetData.setFormulaCell( maCellData, readCellFormula( rStrm ) );
458         else
459             mrSheetData.setBooleanCell( maCellData, bValue );
460     }
461 }
462 
463 void SheetDataContext::importCellBlank( SequenceInputStream& rStrm, CellType eCellType )
464 {
465     OSL_ENSURE( eCellType != CELLTYPE_FORMULA, "SheetDataContext::importCellBlank - no formula cells supported" );
466     if( readCellHeader( rStrm, eCellType ) )
467         mrSheetData.setBlankCell( maCellData );
468 }
469 
470 void SheetDataContext::importCellDouble( SequenceInputStream& rStrm, CellType eCellType )
471 {
472     if( readCellHeader( rStrm, eCellType ) )
473     {
474         maCellData.mnCellType = XML_n;
475         double fValue = rStrm.readDouble();
476         if( eCellType == CELLTYPE_FORMULA )
477             mrSheetData.setFormulaCell( maCellData, readCellFormula( rStrm ) );
478         else
479             mrSheetData.setValueCell( maCellData, fValue );
480     }
481 }
482 
483 void SheetDataContext::importCellError( SequenceInputStream& rStrm, CellType eCellType )
484 {
485     if( readCellHeader( rStrm, eCellType ) )
486     {
487         maCellData.mnCellType = XML_e;
488         sal_uInt8 nErrorCode = rStrm.readuInt8();
489         if( eCellType == CELLTYPE_FORMULA )
490             mrSheetData.setFormulaCell( maCellData, readCellFormula( rStrm ) );
491         else
492             mrSheetData.setErrorCell( maCellData, nErrorCode );
493     }
494 }
495 
496 void SheetDataContext::importCellRk( SequenceInputStream& rStrm, CellType eCellType )
497 {
498     OSL_ENSURE( eCellType != CELLTYPE_FORMULA, "SheetDataContext::importCellRk - no formula cells supported" );
499     if( readCellHeader( rStrm, eCellType ) )
500     {
501         maCellData.mnCellType = XML_n;
502         mrSheetData.setValueCell( maCellData, BiffHelper::calcDoubleFromRk( rStrm.readInt32() ) );
503     }
504 }
505 
506 void SheetDataContext::importCellRString( SequenceInputStream& rStrm, CellType eCellType )
507 {
508     OSL_ENSURE( eCellType != CELLTYPE_FORMULA, "SheetDataContext::importCellRString - no formula cells supported" );
509     if( readCellHeader( rStrm, eCellType ) )
510     {
511         maCellData.mnCellType = XML_inlineStr;
512         RichStringRef xString( new RichString( *this ) );
513         xString->importString( rStrm, true );
514         xString->finalizeImport();
515         mrSheetData.setStringCell( maCellData, xString );
516     }
517 }
518 
519 void SheetDataContext::importCellSi( SequenceInputStream& rStrm, CellType eCellType )
520 {
521     OSL_ENSURE( eCellType != CELLTYPE_FORMULA, "SheetDataContext::importCellSi - no formula cells supported" );
522     if( readCellHeader( rStrm, eCellType ) )
523     {
524         maCellData.mnCellType = XML_s;
525         mrSheetData.setStringCell( maCellData, rStrm.readInt32() );
526     }
527 }
528 
529 void SheetDataContext::importCellString( SequenceInputStream& rStrm, CellType eCellType )
530 {
531     if( readCellHeader( rStrm, eCellType ) )
532     {
533         maCellData.mnCellType = XML_inlineStr;
534         // always import the string, stream will point to formula afterwards, if existing
535         RichStringRef xString( new RichString( *this ) );
536         xString->importString( rStrm, false );
537         xString->finalizeImport();
538         if( eCellType == CELLTYPE_FORMULA )
539             mrSheetData.setFormulaCell( maCellData, readCellFormula( rStrm ) );
540         else
541             mrSheetData.setStringCell( maCellData, xString );
542     }
543 }
544 
545 void SheetDataContext::importArray( SequenceInputStream& rStrm )
546 {
547     if( readFormulaRef( rStrm ) && maFmlaData.isValidArrayRef( maCellData.maCellAddr ) )
548     {
549         rStrm.skip( 1 );
550         ApiTokenSequence aTokens = mrFormulaParser.importFormula( maCellData.maCellAddr, FORMULATYPE_ARRAY, rStrm );
551         mrSheetData.createArrayFormula( maFmlaData.maFormulaRef, aTokens );
552     }
553 }
554 
555 void SheetDataContext::importDataTable( SequenceInputStream& rStrm )
556 {
557     if( readFormulaRef( rStrm ) )
558     {
559         BinAddress aRef1, aRef2;
560         sal_uInt8 nFlags;
561         rStrm >> aRef1 >> aRef2 >> nFlags;
562         maTableData.maRef1        = FormulaProcessorBase::generateAddress2dString( aRef1, false );
563         maTableData.maRef2        = FormulaProcessorBase::generateAddress2dString( aRef2, false );
564         maTableData.mbRowTable    = getFlag( nFlags, BIFF12_DATATABLE_ROW );
565         maTableData.mb2dTable     = getFlag( nFlags, BIFF12_DATATABLE_2D );
566         maTableData.mbRef1Deleted = getFlag( nFlags, BIFF12_DATATABLE_REF1DEL );
567         maTableData.mbRef2Deleted = getFlag( nFlags, BIFF12_DATATABLE_REF2DEL );
568         mrSheetData.createTableOperation( maFmlaData.maFormulaRef, maTableData );
569     }
570 }
571 
572 void SheetDataContext::importSharedFmla( SequenceInputStream& rStrm )
573 {
574     if( readFormulaRef( rStrm ) && maFmlaData.isValidSharedRef( maCellData.maCellAddr ) )
575     {
576         ApiTokenSequence aTokens = mrFormulaParser.importFormula( maCellData.maCellAddr, FORMULATYPE_SHAREDFORMULA, rStrm );
577         mrSheetData.createSharedFormula( maCellData.maCellAddr, aTokens );
578     }
579 }
580 
581 // ============================================================================
582 
583 BiffSheetDataContext::BiffSheetDataContext( const WorksheetHelper& rHelper ) :
584     BiffWorksheetContextBase( rHelper ),
585     SheetDataContextBase( rHelper ),
586     mnBiff2XfId( 0 )
587 {
588     switch( getBiff() )
589     {
590         case BIFF2:
591             mnFormulaSkipSize = 9;  // double formula result, 1 byte flags
592             mnArraySkipSize = 1;    // recalc-always flag
593         break;
594         case BIFF3:
595         case BIFF4:
596             mnFormulaSkipSize = 10; // double formula result, 2 byte flags
597             mnArraySkipSize = 2;    // 2 byte flags
598         break;
599         case BIFF5:
600         case BIFF8:
601             mnFormulaSkipSize = 14; // double formula result, 2 byte flags, 4 bytes nothing
602             mnArraySkipSize = 6;    // 2 byte flags, 4 bytes nothing
603         break;
604         case BIFF_UNKNOWN:
605         break;
606     }
607 }
608 
609 void BiffSheetDataContext::importRecord( BiffInputStream& rStrm )
610 {
611     sal_uInt16 nRecId = rStrm.getRecId();
612     switch( nRecId )
613     {
614         // records in all BIFF versions
615         case BIFF2_ID_ARRAY:        // #i72713#
616         case BIFF3_ID_ARRAY:        importArray( rStrm );   break;
617         case BIFF2_ID_BLANK:
618         case BIFF3_ID_BLANK:        importBlank( rStrm );   break;
619         case BIFF2_ID_BOOLERR:
620         case BIFF3_ID_BOOLERR:      importBoolErr( rStrm ); break;
621         case BIFF2_ID_INTEGER:      importInteger( rStrm ); break;
622         case BIFF_ID_IXFE:          rStrm >> mnBiff2XfId;   break;
623         case BIFF2_ID_LABEL:
624         case BIFF3_ID_LABEL:        importLabel( rStrm );   break;
625         case BIFF2_ID_NUMBER:
626         case BIFF3_ID_NUMBER:       importNumber( rStrm );  break;
627         case BIFF_ID_RK:            importRk( rStrm );      break;
628 
629         // BIFF specific records
630         default: switch( getBiff() )
631         {
632             case BIFF2: switch( nRecId )
633             {
634                 case BIFF2_ID_DATATABLE:    importDataTable( rStrm );   break;
635                 case BIFF2_ID_DATATABLE2:   importDataTable( rStrm );   break;
636                 case BIFF2_ID_FORMULA:      importFormula( rStrm );     break;
637                 case BIFF2_ID_ROW:          importRow( rStrm );         break;
638             }
639             break;
640 
641             case BIFF3: switch( nRecId )
642             {
643                 case BIFF3_ID_DATATABLE:    importDataTable( rStrm );   break;
644                 case BIFF3_ID_FORMULA:      importFormula( rStrm );     break;
645                 case BIFF3_ID_ROW:          importRow( rStrm );         break;
646             }
647             break;
648 
649             case BIFF4: switch( nRecId )
650             {
651                 case BIFF3_ID_DATATABLE:    importDataTable( rStrm );   break;
652                 case BIFF4_ID_FORMULA:      importFormula( rStrm );     break;
653                 case BIFF3_ID_ROW:          importRow( rStrm );         break;
654             }
655             break;
656 
657             case BIFF5: switch( nRecId )
658             {
659                 case BIFF3_ID_DATATABLE:    importDataTable( rStrm );   break;
660                 case BIFF3_ID_FORMULA:
661                 case BIFF4_ID_FORMULA:
662                 case BIFF5_ID_FORMULA:      importFormula( rStrm );     break;
663                 case BIFF_ID_MULTBLANK:     importMultBlank( rStrm );   break;
664                 case BIFF_ID_MULTRK:        importMultRk( rStrm );      break;
665                 case BIFF3_ID_ROW:          importRow( rStrm );         break;
666                 case BIFF_ID_RSTRING:       importLabel( rStrm );       break;
667                 case BIFF_ID_SHAREDFMLA:    importSharedFmla( rStrm );  break;
668             }
669             break;
670 
671             case BIFF8: switch( nRecId )
672             {
673                 case BIFF3_ID_DATATABLE:    importDataTable( rStrm );   break;
674                 case BIFF3_ID_FORMULA:
675                 case BIFF4_ID_FORMULA:
676                 case BIFF5_ID_FORMULA:      importFormula( rStrm );     break;
677                 case BIFF_ID_LABELSST:      importLabelSst( rStrm );    break;
678                 case BIFF_ID_MULTBLANK:     importMultBlank( rStrm );   break;
679                 case BIFF_ID_MULTRK:        importMultRk( rStrm );      break;
680                 case BIFF3_ID_ROW:          importRow( rStrm );         break;
681                 case BIFF_ID_RSTRING:       importLabel( rStrm );       break;
682                 case BIFF_ID_SHAREDFMLA:    importSharedFmla( rStrm );  break;
683             }
684             break;
685 
686             case BIFF_UNKNOWN:
687             break;
688         }
689     }
690 }
691 
692 // private --------------------------------------------------------------------
693 
694 void BiffSheetDataContext::importRow( BiffInputStream& rStrm )
695 {
696     RowModel aModel;
697     sal_uInt16 nRow, nFirstUsedCol, nFirstFreeCol, nHeight;
698     rStrm >> nRow >> nFirstUsedCol >> nFirstFreeCol >> nHeight;
699     if( getBiff() == BIFF2 )
700     {
701         rStrm.skip( 2 );
702         aModel.mbCustomFormat = rStrm.readuInt8() == BIFF2_ROW_CUSTOMFORMAT;
703         if( aModel.mbCustomFormat )
704         {
705             rStrm.skip( 5 );
706             aModel.mnXfId = rStrm.readuInt16();
707         }
708     }
709     else
710     {
711         rStrm.skip( 4 );
712         sal_uInt32 nFlags = rStrm.readuInt32();
713         aModel.mnXfId         = extractValue< sal_Int32 >( nFlags, 16, 12 );
714         aModel.mnLevel        = extractValue< sal_Int32 >( nFlags, 0, 3 );
715         aModel.mbCustomFormat = getFlag( nFlags, BIFF_ROW_CUSTOMFORMAT );
716         aModel.mbCustomHeight = getFlag( nFlags, BIFF_ROW_CUSTOMHEIGHT );
717         aModel.mbShowPhonetic = getFlag( nFlags, BIFF_ROW_SHOWPHONETIC );
718         aModel.mbHidden       = getFlag( nFlags, BIFF_ROW_HIDDEN );
719         aModel.mbCollapsed    = getFlag( nFlags, BIFF_ROW_COLLAPSED );
720         aModel.mbThickTop     = getFlag( nFlags, BIFF_ROW_THICKTOP );
721         aModel.mbThickBottom  = getFlag( nFlags, BIFF_ROW_THICKBOTTOM );
722     }
723 
724     // row index is 0-based in BIFF, but RowModel expects 1-based
725     aModel.mnRow = static_cast< sal_Int32 >( nRow ) + 1;
726     // row height is in twips in BIFF, convert to points
727     aModel.mfHeight = (nHeight & BIFF_ROW_HEIGHTMASK) / 20.0;
728     // set column spans
729     if( nFirstUsedCol < nFirstFreeCol )
730     {
731         sal_Int32 nLastCol = ::std::min< sal_Int32 >( nFirstFreeCol - 1, mrAddressConv.getMaxApiAddress().Column );
732         aModel.insertColSpan( ValueRange( nFirstUsedCol, nLastCol ) );
733     }
734 
735     // set row properties in the current sheet
736     setRowModel( aModel );
737 }
738 
739 bool BiffSheetDataContext::readCellXfId( BiffInputStream& rStrm, const BinAddress& rAddr, bool bBiff2 )
740 {
741     bool bValidAddr = mrAddressConv.convertToCellAddress( maCellData.maCellAddr, rAddr, mnSheet, true );
742     if( bValidAddr )
743     {
744         // update used area of the sheet
745         extendUsedArea( maCellData.maCellAddr );
746 
747         // load the XF identifier according to current BIFF version
748         if( bBiff2 )
749         {
750             /*  #i71453# On first call, check if the file contains XF records
751                 (by trying to access the first XF with index 0). If there are
752                 no XFs, the explicit formatting information contained in each
753                 cell record will be used instead. */
754             if( !mobBiff2HasXfs )
755                 mobBiff2HasXfs = getStyles().getCellXf( 0 ).get() != 0;
756             // read formatting information (includes the XF identifier)
757             sal_uInt8 nFlags1, nFlags2, nFlags3;
758             rStrm >> nFlags1 >> nFlags2 >> nFlags3;
759             /*  If the file contains XFs, extract and set the XF identifier,
760                 otherwise get the explicit formatting. */
761             if( mobBiff2HasXfs.get() )
762             {
763                 maCellData.mnXfId = extractValue< sal_Int32 >( nFlags1, 0, 6 );
764                 /*  If the identifier is equal to 63, then the real identifier
765                     is contained in the preceding IXFE record (stored in the
766                     class member mnBiff2XfId). */
767                 if( maCellData.mnXfId == BIFF2_CELL_USEIXFE )
768                     maCellData.mnXfId = mnBiff2XfId;
769             }
770             else
771             {
772                 /*  Let the Xf class do the API conversion. Keeping the member
773                     maCellData.mnXfId untouched will prevent to trigger the
774                     usual XF formatting conversion later on. */
775                 PropertySet aPropSet( getCell( maCellData.maCellAddr ) );
776                 Xf::writeBiff2CellFormatToPropertySet( *this, aPropSet, nFlags1, nFlags2, nFlags3 );
777             }
778         }
779         else
780         {
781             // BIFF3-BIFF8: 16-bit XF identifier
782             maCellData.mnXfId = rStrm.readuInt16();
783         }
784     }
785     return bValidAddr;
786 }
787 
788 bool BiffSheetDataContext::readCellHeader( BiffInputStream& rStrm, bool bBiff2 )
789 {
790     BinAddress aAddr;
791     rStrm >> aAddr;
792     return readCellXfId( rStrm, aAddr, bBiff2 );
793 }
794 
795 bool BiffSheetDataContext::readFormulaRef( BiffInputStream& rStrm )
796 {
797     BinRange aRange;
798     aRange.read( rStrm, false );    // columns always 8-bit
799     return mrAddressConv.convertToCellRange( maFmlaData.maFormulaRef, aRange, mnSheet, true, true );
800 }
801 
802 void BiffSheetDataContext::importBlank( BiffInputStream& rStrm )
803 {
804     if( readCellHeader( rStrm, rStrm.getRecId() == BIFF2_ID_BLANK ) )
805         mrSheetData.setBlankCell( maCellData );
806 }
807 
808 void BiffSheetDataContext::importBoolErr( BiffInputStream& rStrm )
809 {
810     if( readCellHeader( rStrm, rStrm.getRecId() == BIFF2_ID_BOOLERR ) )
811     {
812         sal_uInt8 nValue, nType;
813         rStrm >> nValue >> nType;
814         switch( nType )
815         {
816             case BIFF_BOOLERR_BOOL:
817                 maCellData.mnCellType = XML_b;
818                 mrSheetData.setBooleanCell( maCellData, nValue != 0 );
819             break;
820             case BIFF_BOOLERR_ERROR:
821                 maCellData.mnCellType = XML_e;
822                 mrSheetData.setErrorCell( maCellData, nValue );
823             break;
824             default:
825                 OSL_ENSURE( false, "BiffSheetDataContext::importBoolErr - unknown cell type" );
826                 maCellData.mnCellType = XML_TOKEN_INVALID;
827                 mrSheetData.setBlankCell( maCellData );
828         }
829     }
830 }
831 
832 void BiffSheetDataContext::importFormula( BiffInputStream& rStrm )
833 {
834     if( readCellHeader( rStrm, getBiff() == BIFF2 ) )
835     {
836         maCellData.mnCellType = XML_n;
837         rStrm.skip( mnFormulaSkipSize );
838         ApiTokenSequence aTokens = mrFormulaParser.importFormula( maCellData.maCellAddr, FORMULATYPE_CELL, rStrm );
839         mrSheetData.setFormulaCell( maCellData, aTokens );
840     }
841 }
842 
843 void BiffSheetDataContext::importInteger( BiffInputStream& rStrm )
844 {
845     if( readCellHeader( rStrm, true ) )
846     {
847         maCellData.mnCellType = XML_n;
848         mrSheetData.setValueCell( maCellData, rStrm.readuInt16() );
849     }
850 }
851 
852 void BiffSheetDataContext::importLabel( BiffInputStream& rStrm )
853 {
854     /*  the deep secrets of BIFF type and record identifier...
855         record id   BIFF    ->  XF type     String type
856         0x0004      2-7     ->  3 byte      8-bit length, byte string
857         0x0004      8       ->  3 byte      16-bit length, unicode string
858         0x0204      2-7     ->  2 byte      16-bit length, byte string
859         0x0204      8       ->  2 byte      16-bit length, unicode string
860      */
861     bool bBiff2Xf = rStrm.getRecId() == BIFF2_ID_LABEL;
862     if( readCellHeader( rStrm, bBiff2Xf ) )
863     {
864         maCellData.mnCellType = XML_inlineStr;
865         if( getBiff() == BIFF8 )
866         {
867             // string may contain rich-text formatting
868             RichStringRef xString( new RichString( *this ) );
869             xString->importUniString( rStrm );
870             xString->finalizeImport();
871             mrSheetData.setStringCell( maCellData, xString );
872         }
873         else
874         {
875             // #i63105# use text encoding from FONT record
876             rtl_TextEncoding eTextEnc = getTextEncoding();
877             if( const Font* pFont = getStyles().getFontFromCellXf( maCellData.mnXfId ).get() )
878                 eTextEnc = pFont->getFontEncoding();
879             // RSTRING record contains rich-text formatting
880             if( rStrm.getRecId() == BIFF_ID_RSTRING )
881             {
882                 BiffStringFlags nFlags = BIFF_STR_EXTRAFONTS;
883                 // BIFF2 record identifier: 8-bit string length (see above)
884                 setFlag( nFlags, BIFF_STR_8BITLENGTH, bBiff2Xf );
885                 RichStringRef xString( new RichString( *this ) );
886                 xString->importByteString( rStrm, eTextEnc, nFlags );
887                 xString->finalizeImport();
888                 mrSheetData.setStringCell( maCellData, xString );
889             }
890             else
891             {
892                 // BIFF2 record identifier: 8-bit string length (see above)
893                 OUString aText = rStrm.readByteStringUC( !bBiff2Xf, eTextEnc );
894                 mrSheetData.setStringCell( maCellData, aText );
895             }
896         }
897     }
898 }
899 
900 void BiffSheetDataContext::importLabelSst( BiffInputStream& rStrm )
901 {
902     if( readCellHeader( rStrm, false ) )
903     {
904         maCellData.mnCellType = XML_s;
905         mrSheetData.setStringCell( maCellData, rStrm.readInt32() );
906     }
907 }
908 
909 void BiffSheetDataContext::importMultBlank( BiffInputStream& rStrm )
910 {
911     BinAddress aAddr;
912     bool bValidAddr = true;
913     for( rStrm >> aAddr; bValidAddr && (rStrm.getRemaining() > 2); ++aAddr.mnCol )
914         if( (bValidAddr = readCellXfId( rStrm, aAddr, false )) == true )
915             mrSheetData.setBlankCell( maCellData );
916 }
917 
918 void BiffSheetDataContext::importMultRk( BiffInputStream& rStrm )
919 {
920     BinAddress aAddr;
921     bool bValidAddr = true;
922     for( rStrm >> aAddr; bValidAddr && (rStrm.getRemaining() > 2); ++aAddr.mnCol )
923     {
924         if( (bValidAddr = readCellXfId( rStrm, aAddr, false )) == true )
925         {
926             maCellData.mnCellType = XML_n;
927             sal_Int32 nRkValue = rStrm.readInt32();
928             mrSheetData.setValueCell( maCellData, BiffHelper::calcDoubleFromRk( nRkValue ) );
929         }
930     }
931 }
932 
933 void BiffSheetDataContext::importNumber( BiffInputStream& rStrm )
934 {
935     if( readCellHeader( rStrm, rStrm.getRecId() == BIFF2_ID_NUMBER ) )
936     {
937         maCellData.mnCellType = XML_n;
938         mrSheetData.setValueCell( maCellData, rStrm.readDouble() );
939     }
940 }
941 
942 void BiffSheetDataContext::importRk( BiffInputStream& rStrm )
943 {
944     if( readCellHeader( rStrm, false ) )
945     {
946         maCellData.mnCellType = XML_n;
947         mrSheetData.setValueCell( maCellData, BiffHelper::calcDoubleFromRk( rStrm.readInt32() ) );
948     }
949 }
950 
951 void BiffSheetDataContext::importArray( BiffInputStream& rStrm )
952 {
953     if( readFormulaRef( rStrm ) && maFmlaData.isValidArrayRef( maCellData.maCellAddr ) )
954     {
955         rStrm.skip( mnArraySkipSize );
956         ApiTokenSequence aTokens = mrFormulaParser.importFormula( maCellData.maCellAddr, FORMULATYPE_ARRAY, rStrm );
957         mrSheetData.createArrayFormula( maFmlaData.maFormulaRef, aTokens );
958     }
959 }
960 
961 void BiffSheetDataContext::importDataTable( BiffInputStream& rStrm )
962 {
963     if( readFormulaRef( rStrm ) )
964     {
965         DataTableModel aModel;
966         BinAddress aRef1, aRef2;
967         switch( rStrm.getRecId() )
968         {
969             case BIFF2_ID_DATATABLE:
970                 rStrm.skip( 1 );
971                 aModel.mbRowTable = rStrm.readuInt8() != 0;
972                 aModel.mb2dTable = false;
973                 rStrm >> aRef1;
974             break;
975             case BIFF2_ID_DATATABLE2:
976                 rStrm.skip( 2 );
977                 aModel.mb2dTable = true;
978                 rStrm >> aRef1 >> aRef2;
979             break;
980             case BIFF3_ID_DATATABLE:
981             {
982                 sal_uInt16 nFlags;
983                 rStrm >> nFlags >> aRef1 >> aRef2;
984                 aModel.mbRowTable = getFlag( nFlags, BIFF_DATATABLE_ROW );
985                 aModel.mb2dTable = getFlag( nFlags, BIFF_DATATABLE_2D );
986                 aModel.mbRef1Deleted = getFlag( nFlags, BIFF_DATATABLE_REF1DEL );
987                 aModel.mbRef2Deleted = getFlag( nFlags, BIFF_DATATABLE_REF2DEL );
988             }
989             break;
990             default:
991                 OSL_ENSURE( false, "BiffSheetDataContext::importDataTable - unknown record id" );
992         }
993         aModel.maRef1 = FormulaProcessorBase::generateAddress2dString( aRef1, false );
994         aModel.maRef2 = FormulaProcessorBase::generateAddress2dString( aRef2, false );
995         mrSheetData.createTableOperation( maFmlaData.maFormulaRef, aModel );
996     }
997 }
998 
999 void BiffSheetDataContext::importSharedFmla( BiffInputStream& rStrm )
1000 {
1001     if( readFormulaRef( rStrm ) && maFmlaData.isValidSharedRef( maCellData.maCellAddr ) )
1002     {
1003         rStrm.skip( 2 );    // flags
1004         ApiTokenSequence aTokens = mrFormulaParser.importFormula( maCellData.maCellAddr, FORMULATYPE_SHAREDFORMULA, rStrm );
1005         mrSheetData.createSharedFormula( maCellData.maCellAddr, aTokens );
1006     }
1007 }
1008 
1009 // ============================================================================
1010 
1011 } // namespace xls
1012 } // namespace oox
1013