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