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/workbookfragment.hxx"
25 
26 #include <com/sun/star/table/CellAddress.hpp>
27 #include "oox/core/filterbase.hxx"
28 #include "oox/drawingml/themefragmenthandler.hxx"
29 #include "oox/helper/attributelist.hxx"
30 #include "oox/helper/progressbar.hxx"
31 #include "oox/helper/propertyset.hxx"
32 #include "oox/ole/olestorage.hxx"
33 #include "oox/xls/biffinputstream.hxx"
34 #include "oox/xls/chartsheetfragment.hxx"
35 #include "oox/xls/connectionsfragment.hxx"
36 #include "oox/xls/externallinkbuffer.hxx"
37 #include "oox/xls/externallinkfragment.hxx"
38 #include "oox/xls/pivotcachebuffer.hxx"
39 #include "oox/xls/sharedstringsbuffer.hxx"
40 #include "oox/xls/sharedstringsfragment.hxx"
41 #include "oox/xls/stylesfragment.hxx"
42 #include "oox/xls/tablebuffer.hxx"
43 #include "oox/xls/themebuffer.hxx"
44 #include "oox/xls/viewsettings.hxx"
45 #include "oox/xls/workbooksettings.hxx"
46 #include "oox/xls/worksheetbuffer.hxx"
47 #include "oox/xls/worksheetfragment.hxx"
48 
49 namespace oox {
50 namespace xls {
51 
52 // ============================================================================
53 
54 using namespace ::com::sun::star::io;
55 using namespace ::com::sun::star::table;
56 using namespace ::com::sun::star::uno;
57 using namespace ::oox::core;
58 
59 using ::oox::drawingml::ThemeFragmentHandler;
60 using ::rtl::OUString;
61 
62 // ============================================================================
63 
64 namespace {
65 
66 const double PROGRESS_LENGTH_GLOBALS        = 0.1;      /// 10% of progress bar for globals import.
67 
68 } // namespace
69 
70 // ============================================================================
71 
WorkbookFragment(const WorkbookHelper & rHelper,const OUString & rFragmentPath)72 WorkbookFragment::WorkbookFragment( const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
73     WorkbookFragmentBase( rHelper, rFragmentPath )
74 {
75 }
76 
onCreateContext(sal_Int32 nElement,const AttributeList & rAttribs)77 ContextHandlerRef WorkbookFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
78 {
79     switch( getCurrentElement() )
80     {
81         case XML_ROOT_CONTEXT:
82             if( nElement == XLS_TOKEN( workbook ) ) return this;
83         break;
84 
85         case XLS_TOKEN( workbook ):
86             switch( nElement )
87             {
88                 case XLS_TOKEN( sheets ):
89                 case XLS_TOKEN( bookViews ):
90                 case XLS_TOKEN( externalReferences ):
91                 case XLS_TOKEN( definedNames ):
92                 case XLS_TOKEN( pivotCaches ):          return this;
93 
94                 case XLS_TOKEN( fileSharing ):          getWorkbookSettings().importFileSharing( rAttribs );    break;
95                 case XLS_TOKEN( workbookPr ):           getWorkbookSettings().importWorkbookPr( rAttribs );     break;
96                 case XLS_TOKEN( calcPr ):               getWorkbookSettings().importCalcPr( rAttribs );         break;
97                 case XLS_TOKEN( oleSize ):              getViewSettings().importOleSize( rAttribs );            break;
98             }
99         break;
100 
101         case XLS_TOKEN( sheets ):
102             if( nElement == XLS_TOKEN( sheet ) ) getWorksheets().importSheet( rAttribs );
103         break;
104         case XLS_TOKEN( bookViews ):
105             if( nElement == XLS_TOKEN( workbookView ) ) getViewSettings().importWorkbookView( rAttribs );
106         break;
107         case XLS_TOKEN( externalReferences ):
108             if( nElement == XLS_TOKEN( externalReference ) ) importExternalReference( rAttribs );
109         break;
110         case XLS_TOKEN( definedNames ):
111             if( nElement == XLS_TOKEN( definedName ) ) { importDefinedName( rAttribs ); return this; } // collect formula
112         break;
113         case XLS_TOKEN( pivotCaches ):
114             if( nElement == XLS_TOKEN( pivotCache ) ) importPivotCache( rAttribs );
115         break;
116     }
117     return 0;
118 }
119 
onCharacters(const OUString & rChars)120 void WorkbookFragment::onCharacters( const OUString& rChars )
121 {
122     if( isCurrentElement( XLS_TOKEN( definedName ) ) && mxCurrName.get() )
123         mxCurrName->setFormula( rChars );
124 }
125 
onCreateRecordContext(sal_Int32 nRecId,SequenceInputStream & rStrm)126 ContextHandlerRef WorkbookFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
127 {
128     switch( getCurrentElement() )
129     {
130         case XML_ROOT_CONTEXT:
131             if( nRecId == BIFF12_ID_WORKBOOK ) return this;
132         break;
133 
134         case BIFF12_ID_WORKBOOK:
135             switch( nRecId )
136             {
137                 case BIFF12_ID_SHEETS:
138                 case BIFF12_ID_BOOKVIEWS:
139                 case BIFF12_ID_EXTERNALREFS:
140                 case BIFF12_ID_PIVOTCACHES:     return this;
141 
142                 case BIFF12_ID_FILESHARING:     getWorkbookSettings().importFileSharing( rStrm );   break;
143                 case BIFF12_ID_WORKBOOKPR:      getWorkbookSettings().importWorkbookPr( rStrm );    break;
144                 case BIFF12_ID_CALCPR:          getWorkbookSettings().importCalcPr( rStrm );        break;
145                 case BIFF12_ID_OLESIZE:         getViewSettings().importOleSize( rStrm );           break;
146                 case BIFF12_ID_DEFINEDNAME:     getDefinedNames().importDefinedName( rStrm );       break;
147             }
148         break;
149 
150         case BIFF12_ID_SHEETS:
151             if( nRecId == BIFF12_ID_SHEET ) getWorksheets().importSheet( rStrm );
152         break;
153         case BIFF12_ID_BOOKVIEWS:
154             if( nRecId == BIFF12_ID_WORKBOOKVIEW ) getViewSettings().importWorkbookView( rStrm );
155         break;
156 
157         case BIFF12_ID_EXTERNALREFS:
158             switch( nRecId )
159             {
160                 case BIFF12_ID_EXTERNALREF:     importExternalRef( rStrm );                         break;
161                 case BIFF12_ID_EXTERNALSELF:    getExternalLinks().importExternalSelf( rStrm );     break;
162                 case BIFF12_ID_EXTERNALSAME:    getExternalLinks().importExternalSame( rStrm );     break;
163                 case BIFF12_ID_EXTERNALADDIN:   getExternalLinks().importExternalAddin( rStrm );    break;
164                 case BIFF12_ID_EXTERNALSHEETS:  getExternalLinks().importExternalSheets( rStrm );   break;
165             }
166         break;
167 
168         case BIFF12_ID_PIVOTCACHES:
169             if( nRecId == BIFF12_ID_PIVOTCACHE ) importPivotCache( rStrm );
170     }
171     return 0;
172 }
173 
getRecordInfos() const174 const RecordInfo* WorkbookFragment::getRecordInfos() const
175 {
176     static const RecordInfo spRecInfos[] =
177     {
178         { BIFF12_ID_BOOKVIEWS,      BIFF12_ID_BOOKVIEWS + 1         },
179         { BIFF12_ID_EXTERNALREFS,   BIFF12_ID_EXTERNALREFS + 1      },
180         { BIFF12_ID_FUNCTIONGROUPS, BIFF12_ID_FUNCTIONGROUPS + 2    },
181         { BIFF12_ID_PIVOTCACHE,     BIFF12_ID_PIVOTCACHE + 1        },
182         { BIFF12_ID_PIVOTCACHES,    BIFF12_ID_PIVOTCACHES + 1       },
183         { BIFF12_ID_SHEETS,         BIFF12_ID_SHEETS + 1            },
184         { BIFF12_ID_WORKBOOK,       BIFF12_ID_WORKBOOK + 1          },
185         { -1,                       -1                              }
186     };
187     return spRecInfos;
188 }
189 
finalizeImport()190 void WorkbookFragment::finalizeImport()
191 {
192     ISegmentProgressBarRef xGlobalSegment = getProgressBar().createSegment( PROGRESS_LENGTH_GLOBALS );
193 
194     // read the theme substream
195     OUString aThemeFragmentPath = getFragmentPathFromFirstType( CREATE_OFFICEDOC_RELATION_TYPE( "theme" ) );
196     if( aThemeFragmentPath.getLength() > 0 )
197         importOoxFragment( new ThemeFragmentHandler( getFilter(), aThemeFragmentPath, getTheme() ) );
198     xGlobalSegment->setPosition( 0.25 );
199 
200     // read the styles substream (requires finalized theme buffer)
201     OUString aStylesFragmentPath = getFragmentPathFromFirstType( CREATE_OFFICEDOC_RELATION_TYPE( "styles" ) );
202     if( aStylesFragmentPath.getLength() > 0 )
203         importOoxFragment( new StylesFragment( *this, aStylesFragmentPath ) );
204     xGlobalSegment->setPosition( 0.5 );
205 
206     // read the shared string table substream (requires finalized styles buffer)
207     OUString aSstFragmentPath = getFragmentPathFromFirstType( CREATE_OFFICEDOC_RELATION_TYPE( "sharedStrings" ) );
208     if( aSstFragmentPath.getLength() > 0 )
209         importOoxFragment( new SharedStringsFragment( *this, aSstFragmentPath ) );
210     xGlobalSegment->setPosition( 0.75 );
211 
212     // read the connections substream
213     OUString aConnFragmentPath = getFragmentPathFromFirstType( CREATE_OFFICEDOC_RELATION_TYPE( "connections" ) );
214     if( aConnFragmentPath.getLength() > 0 )
215         importOoxFragment( new ConnectionsFragment( *this, aConnFragmentPath ) );
216     xGlobalSegment->setPosition( 1.0 );
217 
218     /*  Create fragments for all sheets, before importing them. Needed to do
219         some preprocessing in the fragment constructors, e.g. loading the table
220         fragments for all sheets that are needed before the cell formulas are
221         loaded. Additionally, the instances of the WorkbookGlobals structures
222         have to be stored for every sheet. */
223     typedef ::std::pair< WorksheetGlobalsRef, FragmentHandlerRef > SheetFragmentHandler;
224     typedef ::std::vector< SheetFragmentHandler > SheetFragmentVector;
225     SheetFragmentVector aSheetFragments;
226     WorksheetBuffer& rWorksheets = getWorksheets();
227     sal_Int32 nWorksheetCount = rWorksheets.getWorksheetCount();
228     for( sal_Int32 nWorksheet = 0; nWorksheet < nWorksheetCount; ++nWorksheet )
229     {
230         sal_Int16 nCalcSheet = rWorksheets.getCalcSheetIndex( nWorksheet );
231         const Relation* pRelation = getRelations().getRelationFromRelId( rWorksheets.getWorksheetRelId( nWorksheet ) );
232         if( (nCalcSheet >= 0) && pRelation )
233         {
234             // get fragment path of the sheet
235             OUString aFragmentPath = getFragmentPathFromRelation( *pRelation );
236             OSL_ENSURE( aFragmentPath.getLength() > 0, "WorkbookFragment::finalizeImport - cannot access sheet fragment" );
237             if( aFragmentPath.getLength() > 0 )
238             {
239                 double fSegmentLength = getProgressBar().getFreeLength() / (nWorksheetCount - nWorksheet);
240                 ISegmentProgressBarRef xSheetSegment = getProgressBar().createSegment( fSegmentLength );
241 
242                 // get the sheet type according to the relations type
243                 WorksheetType eSheetType = SHEETTYPE_EMPTYSHEET;
244                 if( pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE( "worksheet" ) )
245                     eSheetType = SHEETTYPE_WORKSHEET;
246                 else if( pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE( "chartsheet" ) )
247                     eSheetType = SHEETTYPE_CHARTSHEET;
248                 else if( (pRelation->maType == CREATE_MSOFFICE_RELATION_TYPE( "xlMacrosheet" )) ||
249                          (pRelation->maType == CREATE_MSOFFICE_RELATION_TYPE( "xlIntlMacrosheet" )) )
250                     eSheetType = SHEETTYPE_MACROSHEET;
251                 else if( pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE( "dialogsheet" ) )
252                     eSheetType = SHEETTYPE_DIALOGSHEET;
253                 OSL_ENSURE( eSheetType != SHEETTYPE_EMPTYSHEET, "WorkbookFragment::finalizeImport - unknown sheet type" );
254                 if( eSheetType != SHEETTYPE_EMPTYSHEET )
255                 {
256                     // create the WorksheetGlobals object
257                     WorksheetGlobalsRef xSheetGlob = WorksheetHelper::constructGlobals( *this, xSheetSegment, eSheetType, nCalcSheet );
258                     OSL_ENSURE( xSheetGlob.get(), "WorkbookFragment::finalizeImport - missing sheet in document" );
259                     if( xSheetGlob.get() )
260                     {
261                         // create the sheet fragment handler
262                         ::rtl::Reference< WorksheetFragmentBase > xFragment;
263                         switch( eSheetType )
264                         {
265                             case SHEETTYPE_WORKSHEET:
266                             case SHEETTYPE_MACROSHEET:
267                             case SHEETTYPE_DIALOGSHEET:
268                                 xFragment.set( new WorksheetFragment( *xSheetGlob, aFragmentPath ) );
269                             break;
270                             case SHEETTYPE_CHARTSHEET:
271                                 xFragment.set( new ChartsheetFragment( *xSheetGlob, aFragmentPath ) );
272                             break;
273                             default:
274                                 OSL_ENSURE( false, "WorkbookFragment::finalizeImport - unexpected sheet type" );
275                         }
276 
277                         // insert the fragment into the map
278                         if( xFragment.is() )
279                             aSheetFragments.push_back( SheetFragmentHandler( xSheetGlob, xFragment.get() ) );
280                     }
281                 }
282             }
283         }
284     }
285 
286     // create all defined names and database ranges
287     getDefinedNames().finalizeImport();
288     getTables().finalizeImport();
289 
290     // load all worksheets
291     for( SheetFragmentVector::iterator aIt = aSheetFragments.begin(), aEnd = aSheetFragments.end(); aIt != aEnd; ++aIt )
292     {
293         // import the sheet fragment
294         importOoxFragment( aIt->second );
295         // delete fragment object and WorkbookGlobals object, will free all allocated sheet buffers
296         aIt->second.clear();
297         aIt->first.reset();
298     }
299 
300     // open the VBA project storage
301     OUString aVbaFragmentPath = getFragmentPathFromFirstType( CREATE_MSOFFICE_RELATION_TYPE( "vbaProject" ) );
302     if( aVbaFragmentPath.getLength() > 0 )
303     {
304         Reference< XInputStream > xInStrm = getBaseFilter().openInputStream( aVbaFragmentPath );
305         if( xInStrm.is() )
306             setVbaProjectStorage( StorageRef( new ::oox::ole::OleStorage( getBaseFilter().getComponentContext(), xInStrm, false ) ) );
307     }
308 
309     // final conversions, e.g. calculation settings and view settings
310     finalizeWorkbookImport();
311 }
312 
313 // private --------------------------------------------------------------------
314 
importExternalReference(const AttributeList & rAttribs)315 void WorkbookFragment::importExternalReference( const AttributeList& rAttribs )
316 {
317     if( ExternalLink* pExtLink = getExternalLinks().importExternalReference( rAttribs ).get() )
318         importExternalLinkFragment( *pExtLink );
319 }
320 
importDefinedName(const AttributeList & rAttribs)321 void WorkbookFragment::importDefinedName( const AttributeList& rAttribs )
322 {
323     mxCurrName = getDefinedNames().importDefinedName( rAttribs );
324 }
325 
importPivotCache(const AttributeList & rAttribs)326 void WorkbookFragment::importPivotCache( const AttributeList& rAttribs )
327 {
328     sal_Int32 nCacheId = rAttribs.getInteger( XML_cacheId, -1 );
329     OUString aRelId = rAttribs.getString( R_TOKEN( id ), OUString() );
330     importPivotCacheDefFragment( aRelId, nCacheId );
331 }
332 
importExternalRef(SequenceInputStream & rStrm)333 void WorkbookFragment::importExternalRef( SequenceInputStream& rStrm )
334 {
335     if( ExternalLink* pExtLink = getExternalLinks().importExternalRef( rStrm ).get() )
336         importExternalLinkFragment( *pExtLink );
337 }
338 
importPivotCache(SequenceInputStream & rStrm)339 void WorkbookFragment::importPivotCache( SequenceInputStream& rStrm )
340 {
341     sal_Int32 nCacheId = rStrm.readInt32();
342     OUString aRelId = BiffHelper::readString( rStrm );
343     importPivotCacheDefFragment( aRelId, nCacheId );
344 }
345 
importExternalLinkFragment(ExternalLink & rExtLink)346 void WorkbookFragment::importExternalLinkFragment( ExternalLink& rExtLink )
347 {
348     OUString aFragmentPath = getFragmentPathFromRelId( rExtLink.getRelId() );
349     if( aFragmentPath.getLength() > 0 )
350         importOoxFragment( new ExternalLinkFragment( *this, aFragmentPath, rExtLink ) );
351 }
352 
importPivotCacheDefFragment(const OUString & rRelId,sal_Int32 nCacheId)353 void WorkbookFragment::importPivotCacheDefFragment( const OUString& rRelId, sal_Int32 nCacheId )
354 {
355     // pivot caches will be imported on demand, here we just store the fragment path in the buffer
356     getPivotCaches().registerPivotCacheFragment( nCacheId, getFragmentPathFromRelId( rRelId ) );
357 }
358 
359 // ============================================================================
360 
BiffWorkbookFragment(const WorkbookHelper & rHelper,const OUString & rStrmName)361 BiffWorkbookFragment::BiffWorkbookFragment( const WorkbookHelper& rHelper, const OUString& rStrmName ) :
362     BiffWorkbookFragmentBase( rHelper, rStrmName )
363 {
364 }
365 
importFragment()366 bool BiffWorkbookFragment::importFragment()
367 {
368     bool bRet = false;
369 
370     BiffFragmentType eFragment = startFragment( getBiff() );
371     switch( eFragment )
372     {
373         case BIFF_FRAGMENT_GLOBALS:
374         {
375             BiffInputStream& rStrm = getInputStream();
376             // import workbook globals fragment and create sheets in document
377             ISegmentProgressBarRef xGlobalsProgress = getProgressBar().createSegment( PROGRESS_LENGTH_GLOBALS );
378             bRet = importGlobalsFragment( *xGlobalsProgress );
379             // load sheet fragments (do not return false in bRet on missing/broken sheets)
380             WorksheetBuffer& rWorksheets = getWorksheets();
381             bool bNextSheet = bRet;
382             for( sal_Int32 nWorksheet = 0, nWorksheetCount = rWorksheets.getWorksheetCount(); bNextSheet && (nWorksheet < nWorksheetCount); ++nWorksheet )
383             {
384                 // calculate progress size for the sheet
385                 double fSegmentLength = getProgressBar().getFreeLength() / (nWorksheetCount - nWorksheet);
386                 ISegmentProgressBarRef xSheetProgress = getProgressBar().createSegment( fSegmentLength );
387                 /*  Try to start a new sheet fragment. The SHEET records point to the
388                     first record of the sheet fragment which is usually a BOF record. */
389                 BiffFragmentType eSheetFragment = BIFF_FRAGMENT_UNKNOWN;
390                 sal_Int64 nRecHandle = rWorksheets.getBiffRecordHandle( nWorksheet );
391                 if( rStrm.startRecordByHandle( nRecHandle ) )
392                 {
393                     /*  #i109800# Stream may point to any record of the sheet fragment.
394                         Check the record identifier before calling startFragment(). */
395                     bool bIsBofRec = BiffHelper::isBofRecord( rStrm );
396                     /*  Rewind the record. If it is the BOF record, it will be read in
397                         startFragment(). In every case, stream will point before the
398                         first available non-BOF record. */
399                     rStrm.rewindRecord();
400                     // if the BOF record is missing, a regular worksheet will be assumed
401                     eSheetFragment = bIsBofRec ? startFragment( getBiff() ) : BIFF_FRAGMENT_WORKSHEET;
402                 }
403                 sal_Int16 nCalcSheet = rWorksheets.getCalcSheetIndex( nWorksheet );
404                 bNextSheet = importSheetFragment( *xSheetProgress, eSheetFragment, nCalcSheet );
405             }
406         }
407         break;
408 
409         case BIFF_FRAGMENT_WORKSPACE:
410         {
411             bRet = importWorkspaceFragment();
412             // sheets are embedded in workspace fragment, nothing to do here
413         }
414         break;
415 
416         case BIFF_FRAGMENT_WORKSHEET:
417         case BIFF_FRAGMENT_CHARTSHEET:
418         case BIFF_FRAGMENT_MACROSHEET:
419         {
420             /*  Single sheet without globals
421                 - #i62752# possible in all BIFF versions
422                 - do not return false in bRet on missing/broken sheets. */
423             getWorksheets().initializeSingleSheet();
424             importSheetFragment( getProgressBar(), eFragment, 0 );
425             // success, even if stream is broken
426             bRet = true;
427         }
428         break;
429 
430         default:;
431     }
432 
433     // final conversions, e.g. calculation settings and view settings
434     if( bRet )
435         finalizeWorkbookImport();
436 
437     return bRet;
438 }
439 
importWorkspaceFragment()440 bool BiffWorkbookFragment::importWorkspaceFragment()
441 {
442     // enable workbook mode, has not been set yet in BIFF4 workspace files
443     setIsWorkbookFile();
444 
445     WorksheetBuffer& rWorksheets = getWorksheets();
446     bool bRet = true;
447 
448     // import the workspace globals
449     ISegmentProgressBarRef xGlobalsProgress = getProgressBar().createSegment( PROGRESS_LENGTH_GLOBALS );
450     bool bLoop = true;
451     BiffInputStream& rStrm = getInputStream();
452     while( bRet && bLoop && rStrm.startNextRecord() && (rStrm.getRecId() != BIFF_ID_EOF) )
453     {
454         switch( rStrm.getRecId() )
455         {
456             case BIFF_ID_SHEET:         rWorksheets.importSheet( rStrm );                   break;
457             case BIFF_ID_CODEPAGE:      setCodePage( rStrm.readuInt16() );                  break;
458             case BIFF_ID_FILEPASS:      bRet = getCodecHelper().importFilePass( rStrm );    break;
459             case BIFF_ID_SHEETHEADER:   rStrm.rewindRecord(); bLoop = false;                break;
460         }
461     }
462     xGlobalsProgress->setPosition( 1.0 );
463 
464     // load sheet fragments (do not return false in bRet on missing/broken sheets)
465     bool bNextSheet = bRet;
466     for( sal_Int32 nWorksheet = 0, nWorksheetCount = rWorksheets.getWorksheetCount(); bNextSheet && (nWorksheet < nWorksheetCount); ++nWorksheet )
467     {
468         // try to start a new sheet fragment (with leading SHEETHEADER record)
469         bNextSheet = rStrm.startNextRecord() && (rStrm.getRecId() == BIFF_ID_SHEETHEADER);
470         if( bNextSheet )
471         {
472             double fSegmentLength = getProgressBar().getFreeLength() / (nWorksheetCount - nWorksheet);
473             ISegmentProgressBarRef xSheetProgress = getProgressBar().createSegment( fSegmentLength );
474             /*  Read current sheet name (sheet substreams may not be in the
475                 same order as SHEET records are). */
476             rStrm.skip( 4 );
477             OUString aSheetName = rStrm.readByteStringUC( false, getTextEncoding() );
478             sal_Int16 nCurrSheet = rWorksheets.getCalcSheetIndex( aSheetName );
479             // load the sheet fragment records
480             BiffFragmentType eSheetFragment = startFragment( getBiff() );
481             bNextSheet = importSheetFragment( *xSheetProgress, eSheetFragment, nCurrSheet );
482             // do not return false in bRet on missing/broken sheets
483         }
484     }
485 
486     return bRet;
487 }
488 
importGlobalsFragment(ISegmentProgressBar & rProgressBar)489 bool BiffWorkbookFragment::importGlobalsFragment( ISegmentProgressBar& rProgressBar )
490 {
491     WorkbookSettings& rWorkbookSett = getWorkbookSettings();
492     ViewSettings& rViewSett = getViewSettings();
493     SharedStringsBuffer& rSharedStrings = getSharedStrings();
494     StylesBuffer& rStyles = getStyles();
495     WorksheetBuffer& rWorksheets = getWorksheets();
496     PivotCacheBuffer& rPivotCaches = getPivotCaches();
497     bool bHasVbaProject = false;
498     bool bEmptyVbaProject = false;
499 
500     // collect records that need to be loaded in a second pass
501     typedef ::std::vector< sal_Int64 > RecordHandleVec;
502     RecordHandleVec aExtLinkRecs;
503 
504     bool bRet = true;
505     bool bLoop = true;
506     BiffInputStream& rStrm = getInputStream();
507     while( bRet && bLoop && rStrm.startNextRecord() )
508     {
509         sal_uInt16 nRecId = rStrm.getRecId();
510         bool bExtLinkRec = false;
511 
512         /*  #i56376# BIFF5-BIFF8: If an EOF record for globals is missing,
513             simulate it. The issue is about a document where the sheet fragment
514             starts directly after the EXTSST record, without terminating the
515             globals fragment with an EOF record. */
516         if( BiffHelper::isBofRecord( rStrm ) || (nRecId == BIFF_ID_EOF) )
517         {
518             bLoop = false;
519         }
520         else switch( nRecId )
521         {
522             // records in all BIFF versions
523             case BIFF_ID_CODEPAGE:      setCodePage( rStrm.readuInt16() );                  break;
524             case BIFF_ID_DATEMODE:      rWorkbookSett.importDateMode( rStrm );              break;
525             case BIFF_ID_FILEPASS:      bRet = getCodecHelper().importFilePass( rStrm );    break;
526             case BIFF_ID_PRECISION:     rWorkbookSett.importPrecision( rStrm );             break;
527             case BIFF_ID_WINDOW1:       rViewSett.importWindow1( rStrm );                   break;
528 
529             // BIFF specific records
530             default: switch( getBiff() )
531             {
532                 case BIFF2: switch( nRecId )
533                 {
534                     case BIFF2_ID_DEFINEDNAME:  bExtLinkRec = true;                 break;
535                     case BIFF2_ID_EXTERNALNAME: bExtLinkRec = true;                 break;
536                     case BIFF_ID_EXTERNSHEET:   bExtLinkRec = true;                 break;
537                     case BIFF2_ID_FONT:         rStyles.importFont( rStrm );        break;
538                     case BIFF_ID_FONTCOLOR:     rStyles.importFontColor( rStrm );   break;
539                     case BIFF2_ID_FORMAT:       rStyles.importFormat( rStrm );      break;
540                     case BIFF2_ID_XF:           rStyles.importXf( rStrm );          break;
541                 }
542                 break;
543 
544                 case BIFF3: switch( nRecId )
545                 {
546                     case BIFF_ID_CRN:           bExtLinkRec = true;                         break;
547                     case BIFF3_ID_DEFINEDNAME:  bExtLinkRec = true;                         break;
548                     case BIFF3_ID_EXTERNALNAME: bExtLinkRec = true;                         break;
549                     case BIFF_ID_EXTERNSHEET:   bExtLinkRec = true;                         break;
550                     case BIFF_ID_FILESHARING:   rWorkbookSett.importFileSharing( rStrm );   break;
551                     case BIFF3_ID_FONT:         rStyles.importFont( rStrm );                break;
552                     case BIFF2_ID_FORMAT:       rStyles.importFormat( rStrm );              break;
553                     case BIFF_ID_HIDEOBJ:       rWorkbookSett.importHideObj( rStrm );       break;
554                     case BIFF_ID_PALETTE:       rStyles.importPalette( rStrm );             break;
555                     case BIFF_ID_STYLE:         rStyles.importStyle( rStrm );               break;
556                     case BIFF_ID_XCT:           bExtLinkRec = true;                         break;
557                     case BIFF3_ID_XF:           rStyles.importXf( rStrm );                  break;
558                 }
559                 break;
560 
561                 case BIFF4: switch( nRecId )
562                 {
563                     case BIFF_ID_CRN:           bExtLinkRec = true;                         break;
564                     case BIFF3_ID_DEFINEDNAME:  bExtLinkRec = true;                         break;
565                     case BIFF3_ID_EXTERNALNAME: bExtLinkRec = true;                         break;
566                     case BIFF_ID_EXTERNSHEET:   bExtLinkRec = true;                         break;
567                     case BIFF_ID_FILESHARING:   rWorkbookSett.importFileSharing( rStrm );   break;
568                     case BIFF3_ID_FONT:         rStyles.importFont( rStrm );                break;
569                     case BIFF4_ID_FORMAT:       rStyles.importFormat( rStrm );              break;
570                     case BIFF_ID_HIDEOBJ:       rWorkbookSett.importHideObj( rStrm );       break;
571                     case BIFF_ID_PALETTE:       rStyles.importPalette( rStrm );             break;
572                     case BIFF_ID_STYLE:         rStyles.importStyle( rStrm );               break;
573                     case BIFF_ID_XCT:           bExtLinkRec = true;                         break;
574                     case BIFF4_ID_XF:           rStyles.importXf( rStrm );                  break;
575                 }
576                 break;
577 
578                 case BIFF5: switch( nRecId )
579                 {
580                     case BIFF_ID_BOOKBOOL:      rWorkbookSett.importBookBool( rStrm );      break;
581                     case BIFF_ID_CRN:           bExtLinkRec = true;                         break;
582                     case BIFF5_ID_DEFINEDNAME:  bExtLinkRec = true;                         break;
583                     case BIFF5_ID_EXTERNALNAME: bExtLinkRec = true;                         break;
584                     case BIFF_ID_EXTERNSHEET:   bExtLinkRec = true;                         break;
585                     case BIFF_ID_FILESHARING:   rWorkbookSett.importFileSharing( rStrm );   break;
586                     case BIFF5_ID_FONT:         rStyles.importFont( rStrm );                break;
587                     case BIFF4_ID_FORMAT:       rStyles.importFormat( rStrm );              break;
588                     case BIFF_ID_HIDEOBJ:       rWorkbookSett.importHideObj( rStrm );       break;
589                     case BIFF_ID_OLESIZE:       rViewSett.importOleSize( rStrm );           break;
590                     case BIFF_ID_PALETTE:       rStyles.importPalette( rStrm );             break;
591                     case BIFF_ID_PIVOTCACHE:    rPivotCaches.importPivotCacheRef( rStrm );  break;
592                     case BIFF_ID_SHEET:         rWorksheets.importSheet( rStrm );           break;
593                     case BIFF_ID_STYLE:         rStyles.importStyle( rStrm );               break;
594                     case BIFF_ID_XCT:           bExtLinkRec = true;                         break;
595                     case BIFF5_ID_XF:           rStyles.importXf( rStrm );                  break;
596                 }
597                 break;
598 
599                 case BIFF8: switch( nRecId )
600                 {
601                     case BIFF_ID_BOOKBOOL:          rWorkbookSett.importBookBool( rStrm );      break;
602                     case BIFF_ID_CODENAME:          rWorkbookSett.importCodeName( rStrm );      break;
603                     case BIFF_ID_CRN:               bExtLinkRec = true;                         break;
604                     case BIFF5_ID_DEFINEDNAME:      bExtLinkRec = true;                         break;
605                     case BIFF_ID_EXTERNALBOOK:      bExtLinkRec = true;                         break;
606                     case BIFF5_ID_EXTERNALNAME:     bExtLinkRec = true;                         break;
607                     case BIFF_ID_EXTERNSHEET:       bExtLinkRec = true;                         break;
608                     case BIFF_ID_FILESHARING:       rWorkbookSett.importFileSharing( rStrm );   break;
609                     case BIFF5_ID_FONT:             rStyles.importFont( rStrm );                break;
610                     case BIFF4_ID_FORMAT:           rStyles.importFormat( rStrm );              break;
611                     case BIFF_ID_HIDEOBJ:           rWorkbookSett.importHideObj( rStrm );       break;
612                     case BIFF_ID_OLESIZE:           rViewSett.importOleSize( rStrm );           break;
613                     case BIFF_ID_PALETTE:           rStyles.importPalette( rStrm );             break;
614                     case BIFF_ID_PIVOTCACHE:        rPivotCaches.importPivotCacheRef( rStrm );  break;
615                     case BIFF_ID_SHEET:             rWorksheets.importSheet( rStrm );           break;
616                     case BIFF_ID_SST:               rSharedStrings.importSst( rStrm );          break;
617                     case BIFF_ID_STYLE:             rStyles.importStyle( rStrm );               break;
618                     case BIFF_ID_USESELFS:          rWorkbookSett.importUsesElfs( rStrm );      break;
619                     case BIFF_ID_VBAPROJECT:        bHasVbaProject = true;                      break;
620                     case BIFF_ID_VBAPROJECTEMPTY:   bEmptyVbaProject = true;                    break;
621                     case BIFF_ID_XCT:               bExtLinkRec = true;                         break;
622                     case BIFF5_ID_XF:               rStyles.importXf( rStrm );                  break;
623                 }
624                 break;
625 
626                 case BIFF_UNKNOWN: break;
627             }
628         }
629 
630         if( bExtLinkRec )
631             aExtLinkRecs.push_back( rStrm.getRecHandle() );
632     }
633 
634     // finalize global buffers
635     rProgressBar.setPosition( 0.5 );
636     if( bRet )
637     {
638         rSharedStrings.finalizeImport();
639         rStyles.finalizeImport();
640     }
641 
642     /*  Import external link data (EXTERNSHEET, EXTERNALNAME, DEFINEDNAME)
643         which need existing internal sheets (SHEET records). The SHEET records
644         may follow the external links records in some BIFF versions. */
645     if( bRet && !aExtLinkRecs.empty() )
646     {
647         // remember current stream position (the EOF record)
648         sal_Int64 nEofHandle = rStrm.getRecHandle();
649         // context handler implementing import of external link records
650         BiffExternalSheetDataContext aSheetContext( *this, true );
651         // import all records by using their cached record handle
652         for( RecordHandleVec::const_iterator aIt = aExtLinkRecs.begin(), aEnd = aExtLinkRecs.end(); (aIt != aEnd) && rStrm.startRecordByHandle( *aIt ); ++aIt )
653             aSheetContext.importRecord( rStrm );
654         // finalize global buffers
655         getDefinedNames().finalizeImport();
656         // seek back to the EOF record of the workbook globals fragment
657         bRet = rStrm.startRecordByHandle( nEofHandle );
658     }
659 
660     // open the VBA project storage
661     if( bHasVbaProject && !bEmptyVbaProject )
662         setVbaProjectStorage( getBaseFilter().openSubStorage( CREATE_OUSTRING( "_VBA_PROJECT_CUR" ), false ) );
663 
664     // #i56376# missing EOF - rewind before worksheet BOF record (see above)
665     if( bRet && BiffHelper::isBofRecord( rStrm ) )
666         rStrm.rewindRecord();
667 
668     rProgressBar.setPosition( 1.0 );
669     return bRet;
670 }
671 
importSheetFragment(ISegmentProgressBar & rProgressBar,BiffFragmentType eFragment,sal_Int16 nCalcSheet)672 bool BiffWorkbookFragment::importSheetFragment( ISegmentProgressBar& rProgressBar, BiffFragmentType eFragment, sal_Int16 nCalcSheet )
673 {
674     // no Calc sheet - skip the fragment
675     if( nCalcSheet < 0 )
676         return skipFragment();
677 
678     // find the sheet type for this fragment
679     WorksheetType eSheetType = SHEETTYPE_EMPTYSHEET;
680     switch( eFragment )
681     {
682         case BIFF_FRAGMENT_WORKSHEET:   eSheetType = SHEETTYPE_WORKSHEET;   break;
683         case BIFF_FRAGMENT_CHARTSHEET:  eSheetType = SHEETTYPE_CHARTSHEET;  break;
684         case BIFF_FRAGMENT_MACROSHEET:  eSheetType = SHEETTYPE_MACROSHEET;  break;
685         case BIFF_FRAGMENT_MODULESHEET: eSheetType = SHEETTYPE_MODULESHEET; break;
686         case BIFF_FRAGMENT_EMPTYSHEET:  eSheetType = SHEETTYPE_EMPTYSHEET;  break;
687         default:                        return false;
688     }
689 
690     /*  #i11183# Clear buffers that are used per-sheet, e.g. external links in
691         BIFF4W and BIFF5 files, or defined names in BIFF4W files. */
692     createBuffersPerSheet( nCalcSheet );
693 
694     // preprocess some records
695     BiffInputStream& rStrm = getInputStream();
696     switch( getBiff() )
697     {
698         // load the workbook globals fragment records in BIFF2-BIFF4
699         case BIFF2:
700         case BIFF3:
701         case BIFF4:
702         {
703             // remember current record to seek back below
704             sal_Int64 nRecHandle = rStrm.getRecHandle();
705             // import the global records
706             ISegmentProgressBarRef xGlobalsProgress = rProgressBar.createSegment( PROGRESS_LENGTH_GLOBALS );
707             importGlobalsFragment( *xGlobalsProgress );
708             // rewind stream to fragment BOF record
709             rStrm.startRecordByHandle( nRecHandle );
710         }
711         break;
712 
713         // load the external link records for this sheet in BIFF5
714         case BIFF5:
715         {
716             // remember current record to seek back below
717             sal_Int64 nRecHandle = rStrm.getRecHandle();
718             // fragment implementing import of external link records
719             BiffExternalLinkFragment( *this ).importFragment();
720             // rewind stream to fragment BOF record
721             rStrm.startRecordByHandle( nRecHandle );
722         }
723         break;
724 
725         case BIFF8:
726         break;
727 
728         case BIFF_UNKNOWN:
729         break;
730     }
731 
732     // create the WorksheetGlobals object
733     ISegmentProgressBarRef xSheetProgress = rProgressBar.createSegment( rProgressBar.getFreeLength() );
734     WorksheetGlobalsRef xSheetGlob = WorksheetHelper::constructGlobals( *this, xSheetProgress, eSheetType, nCalcSheet );
735     OSL_ENSURE( xSheetGlob.get(), "BiffWorkbookFragment::importSheetFragment - missing sheet in document" );
736     if( !xSheetGlob.get() )
737         return false;
738 
739     // create the worksheet fragment
740     ::boost::shared_ptr< BiffWorksheetFragmentBase > xFragment;
741     switch( eSheetType )
742     {
743         case SHEETTYPE_WORKSHEET:
744         case SHEETTYPE_MACROSHEET:
745         case SHEETTYPE_DIALOGSHEET:
746             xFragment.reset( new BiffWorksheetFragment( *xSheetGlob, *this ) );
747         break;
748         case SHEETTYPE_CHARTSHEET:
749             xFragment.reset( new BiffChartsheetFragment( *xSheetGlob, *this ) );
750         break;
751         case SHEETTYPE_MODULESHEET:
752         case SHEETTYPE_EMPTYSHEET:
753             xFragment.reset( new BiffSkipWorksheetFragment( *xSheetGlob, *this ) );
754         break;
755     }
756     // load the sheet fragment records
757     return xFragment.get() && xFragment->importFragment();
758 }
759 
760 // ============================================================================
761 
762 } // namespace xls
763 } // namespace oox
764