fpspreadsheet: Cross-platform OLE code now works for most spreadsheets

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@653 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
sekelsenmat 2009-01-07 10:50:16 +00:00
parent 5d89c8cf03
commit 111b3f819c
3 changed files with 218 additions and 213 deletions

View File

@ -6,7 +6,7 @@
<General>
<MainUnit Value="0"/>
<TargetFileExt Value=".exe"/>
<ActiveEditorIndexAtStart Value="4"/>
<ActiveEditorIndexAtStart Value="0"/>
</General>
<VersionInfo>
<ProjectVersion Value=""/>
@ -30,15 +30,15 @@
<PackageName Value="laz_fpspreadsheet"/>
</Item1>
</RequiredPackages>
<Units Count="21">
<Units Count="19">
<Unit0>
<Filename Value="excel5demo.lpr"/>
<IsPartOfProject Value="True"/>
<UnitName Value="excel5demo"/>
<CursorPos X="16" Y="16"/>
<TopLine Value="1"/>
<CursorPos X="1" Y="33"/>
<TopLine Value="28"/>
<EditorIndex Value="0"/>
<UsageCount Value="112"/>
<UsageCount Value="152"/>
<Loaded Value="True"/>
</Unit0>
<Unit1>
@ -46,273 +46,262 @@
<UnitName Value="fpspreadsheet"/>
<CursorPos X="32" Y="414"/>
<TopLine Value="388"/>
<UsageCount Value="19"/>
<UsageCount Value="15"/>
</Unit1>
<Unit2>
<Filename Value="..\xlsbiff5.pas"/>
<UnitName Value="xlsbiff5"/>
<CursorPos X="16" Y="320"/>
<TopLine Value="300"/>
<UsageCount Value="19"/>
<UsageCount Value="15"/>
</Unit2>
<Unit3>
<Filename Value="..\..\..\..\..\FPC220\source\packages\base\winunits\activex.pp"/>
<UnitName Value="ActiveX"/>
<CursorPos X="27" Y="29"/>
<TopLine Value="6"/>
<UsageCount Value="3"/>
</Unit3>
<Unit4>
<Filename Value="..\fpolestorage.pas"/>
<UnitName Value="fpolestorage"/>
<CursorPos X="1" Y="1"/>
<TopLine Value="1"/>
<UsageCount Value="39"/>
</Unit4>
<Unit5>
<Filename Value="..\..\..\..\..\FPC220\source\rtl\inc\objpash.inc"/>
<CursorPos X="21" Y="141"/>
<TopLine Value="131"/>
<UsageCount Value="3"/>
</Unit5>
<Unit6>
<UsageCount Value="35"/>
</Unit3>
<Unit4>
<Filename Value="..\xlsbiff2.pas"/>
<UnitName Value="xlsbiff2"/>
<CursorPos X="20" Y="277"/>
<TopLine Value="260"/>
<UsageCount Value="12"/>
</Unit6>
<Unit7>
<UsageCount Value="8"/>
</Unit4>
<Unit5>
<Filename Value="..\..\..\..\..\FPC220\source\rtl\objpas\classes\classesh.inc"/>
<CursorPos X="22" Y="1602"/>
<TopLine Value="1598"/>
<UsageCount Value="5"/>
</Unit7>
<Unit8>
<Filename Value="..\..\..\..\..\FPC220\source\rtl\win32\buildrtl.pp"/>
<UnitName Value="buildrtl"/>
<CursorPos X="29" Y="5"/>
<TopLine Value="1"/>
<UsageCount Value="3"/>
</Unit8>
<Unit9>
<UsageCount Value="1"/>
</Unit5>
<Unit6>
<Filename Value="..\..\..\..\..\FPC220\source\rtl\objpas\fgl.pp"/>
<UnitName Value="fgl"/>
<CursorPos X="15" Y="86"/>
<TopLine Value="55"/>
<UsageCount Value="12"/>
</Unit9>
<Unit10>
<UsageCount Value="8"/>
</Unit6>
<Unit7>
<Filename Value="..\..\..\..\..\lazarus\lcl\interfaces\win32\win32wsstdctrls.pp"/>
<UnitName Value="Win32WSStdCtrls"/>
<CursorPos X="11" Y="737"/>
<TopLine Value="713"/>
<EditorIndex Value="8"/>
<UsageCount Value="45"/>
<EditorIndex Value="9"/>
<UsageCount Value="66"/>
<Loaded Value="True"/>
</Unit10>
<Unit11>
</Unit7>
<Unit8>
<Filename Value="..\..\..\..\..\lazarus\lcl\interfaces\win32\win32wscontrols.pp"/>
<UnitName Value="Win32WSControls"/>
<CursorPos X="13" Y="206"/>
<TopLine Value="199"/>
<UsageCount Value="6"/>
</Unit11>
<Unit12>
<UsageCount Value="2"/>
</Unit8>
<Unit9>
<Filename Value="..\..\..\..\..\lazarus\components\sqlite\registersqlite3.pas"/>
<UnitName Value="registersqlite3"/>
<CursorPos X="15" Y="5"/>
<TopLine Value="1"/>
<UsageCount Value="5"/>
</Unit12>
<Unit13>
<UsageCount Value="1"/>
</Unit9>
<Unit10>
<Filename Value="..\..\..\..\..\lazarus\ideintf\componenteditors.pas"/>
<UnitName Value="ComponentEditors"/>
<CursorPos X="54" Y="353"/>
<TopLine Value="330"/>
<EditorIndex Value="7"/>
<UsageCount Value="43"/>
<EditorIndex Value="8"/>
<UsageCount Value="64"/>
<Loaded Value="True"/>
</Unit13>
<Unit14>
</Unit10>
<Unit11>
<Filename Value="..\..\..\..\..\FPC220\source\rtl\objpas\classes\cregist.inc"/>
<CursorPos X="17" Y="124"/>
<TopLine Value="121"/>
<UsageCount Value="5"/>
</Unit14>
<Unit15>
<UsageCount Value="1"/>
</Unit11>
<Unit12>
<Filename Value="..\..\xlsbiff5.pas"/>
<UnitName Value="xlsbiff5"/>
<CursorPos X="28" Y="62"/>
<TopLine Value="59"/>
<EditorIndex Value="2"/>
<UsageCount Value="41"/>
<CursorPos X="1" Y="224"/>
<TopLine Value="215"/>
<EditorIndex Value="3"/>
<UsageCount Value="61"/>
<Loaded Value="True"/>
</Unit15>
<Unit16>
</Unit12>
<Unit13>
<Filename Value="..\..\fpsutils.pas"/>
<UnitName Value="fpsutils"/>
<CursorPos X="1" Y="49"/>
<TopLine Value="30"/>
<EditorIndex Value="1"/>
<UsageCount Value="41"/>
<EditorIndex Value="2"/>
<UsageCount Value="61"/>
<Loaded Value="True"/>
</Unit16>
<Unit17>
</Unit13>
<Unit14>
<Filename Value="..\..\xlsbiff2.pas"/>
<UnitName Value="xlsbiff2"/>
<CursorPos X="1" Y="69"/>
<TopLine Value="57"/>
<EditorIndex Value="3"/>
<UsageCount Value="41"/>
<EditorIndex Value="4"/>
<UsageCount Value="61"/>
<Loaded Value="True"/>
</Unit17>
<Unit18>
</Unit14>
<Unit15>
<Filename Value="..\..\fpolestorage.pas"/>
<UnitName Value="fpolestorage"/>
<CursorPos X="1" Y="316"/>
<TopLine Value="308"/>
<EditorIndex Value="4"/>
<UsageCount Value="41"/>
<CursorPos X="37" Y="381"/>
<TopLine Value="367"/>
<EditorIndex Value="5"/>
<UsageCount Value="61"/>
<Loaded Value="True"/>
</Unit18>
<Unit19>
</Unit15>
<Unit16>
<Filename Value="..\..\..\..\..\lazarus26\fpc\2.2.2\source\rtl\objpas\classes\classesh.inc"/>
<CursorPos X="17" Y="724"/>
<TopLine Value="714"/>
<EditorIndex Value="5"/>
<UsageCount Value="11"/>
<EditorIndex Value="6"/>
<UsageCount Value="31"/>
<Loaded Value="True"/>
</Unit19>
<Unit20>
</Unit16>
<Unit17>
<Filename Value="..\..\..\..\..\lazarus26\fpc\2.2.2\source\rtl\objpas\classes\streams.inc"/>
<CursorPos X="1" Y="159"/>
<TopLine Value="151"/>
<EditorIndex Value="6"/>
<EditorIndex Value="7"/>
<UsageCount Value="31"/>
<Loaded Value="True"/>
</Unit17>
<Unit18>
<Filename Value="..\..\fpspreadsheet.pas"/>
<UnitName Value="fpspreadsheet"/>
<CursorPos X="1" Y="319"/>
<TopLine Value="309"/>
<EditorIndex Value="1"/>
<UsageCount Value="11"/>
<Loaded Value="True"/>
</Unit20>
</Unit18>
</Units>
<JumpHistory Count="30" HistoryIndex="29">
<Position1>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="51" Column="25" TopLine="34"/>
<Filename Value="..\..\fpsutils.pas"/>
<Caret Line="45" Column="5" TopLine="26"/>
</Position1>
<Position2>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="157" Column="21" TopLine="151"/>
<Caret Line="150" Column="5" TopLine="131"/>
</Position2>
<Position3>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="155" Column="5" TopLine="136"/>
<Caret Line="191" Column="5" TopLine="172"/>
</Position3>
<Position4>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="65" Column="3" TopLine="58"/>
<Caret Line="49" Column="15" TopLine="39"/>
</Position4>
<Position5>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="25" Column="11" TopLine="16"/>
<Caret Line="331" Column="45" TopLine="326"/>
</Position5>
<Position6>
<Filename Value="..\..\xlsbiff5.pas"/>
<Caret Line="194" Column="15" TopLine="60"/>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="24" Column="27" TopLine="19"/>
</Position6>
<Position7>
<Filename Value="..\..\fpsutils.pas"/>
<Caret Line="45" Column="5" TopLine="26"/>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="247" Column="1" TopLine="229"/>
</Position7>
<Position8>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="150" Column="5" TopLine="131"/>
<Caret Line="224" Column="5" TopLine="205"/>
</Position8>
<Position9>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="191" Column="5" TopLine="172"/>
<Caret Line="231" Column="1" TopLine="219"/>
</Position9>
<Position10>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="49" Column="15" TopLine="39"/>
<Caret Line="377" Column="5" TopLine="358"/>
</Position10>
<Position11>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="331" Column="45" TopLine="326"/>
<Caret Line="225" Column="1" TopLine="211"/>
</Position11>
<Position12>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="24" Column="27" TopLine="19"/>
<Caret Line="246" Column="12" TopLine="237"/>
</Position12>
<Position13>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="247" Column="1" TopLine="229"/>
<Caret Line="79" Column="16" TopLine="78"/>
</Position13>
<Position14>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="224" Column="5" TopLine="205"/>
<Filename Value="..\..\xlsbiff5.pas"/>
<Caret Line="87" Column="16" TopLine="77"/>
</Position14>
<Position15>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="231" Column="1" TopLine="219"/>
<Filename Value="..\..\xlsbiff5.pas"/>
<Caret Line="218" Column="24" TopLine="207"/>
</Position15>
<Position16>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="377" Column="5" TopLine="358"/>
<Caret Line="235" Column="30" TopLine="224"/>
</Position16>
<Position17>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="225" Column="1" TopLine="211"/>
<Caret Line="80" Column="28" TopLine="68"/>
</Position17>
<Position18>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="246" Column="12" TopLine="237"/>
<Caret Line="40" Column="5" TopLine="31"/>
</Position18>
<Position19>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="79" Column="16" TopLine="78"/>
<Caret Line="472" Column="49" TopLine="461"/>
</Position19>
<Position20>
<Filename Value="..\..\xlsbiff5.pas"/>
<Caret Line="87" Column="16" TopLine="77"/>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="405" Column="5" TopLine="386"/>
</Position20>
<Position21>
<Filename Value="..\..\xlsbiff5.pas"/>
<Caret Line="218" Column="24" TopLine="207"/>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="404" Column="14" TopLine="401"/>
</Position21>
<Position22>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="235" Column="30" TopLine="224"/>
<Caret Line="79" Column="42" TopLine="72"/>
</Position22>
<Position23>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="80" Column="28" TopLine="68"/>
<Caret Line="78" Column="31" TopLine="67"/>
</Position23>
<Position24>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="40" Column="5" TopLine="31"/>
<Caret Line="314" Column="39" TopLine="296"/>
</Position24>
<Position25>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="472" Column="49" TopLine="461"/>
<Filename Value="excel5demo.lpr"/>
<Caret Line="34" Column="1" TopLine="25"/>
</Position25>
<Position26>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="405" Column="5" TopLine="386"/>
<Filename Value="excel5demo.lpr"/>
<Caret Line="35" Column="1" TopLine="25"/>
</Position26>
<Position27>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="404" Column="14" TopLine="401"/>
<Caret Line="479" Column="1" TopLine="469"/>
</Position27>
<Position28>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="79" Column="42" TopLine="72"/>
<Caret Line="470" Column="1" TopLine="460"/>
</Position28>
<Position29>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="78" Column="31" TopLine="67"/>
<Filename Value="..\..\xlsbiff5.pas"/>
<Caret Line="217" Column="3" TopLine="205"/>
</Position29>
<Position30>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="314" Column="39" TopLine="296"/>
<Filename Value="..\..\xlsbiff5.pas"/>
<Caret Line="218" Column="23" TopLine="208"/>
</Position30>
</JumpHistory>
</ProjectOptions>

View File

@ -5,18 +5,17 @@ Writes an OLE document
AUTHORS: Felipe Monteiro de Carvalho
Limitations of this unit for creating OLE documents:
Properties of this unit for creating OLE documents:
* Can only create documents with an array of streams. It's not possible
* Can only create documents with one stream. It's not possible
to create real directory structures like the OLE format supports.
This is no problem for most applications.
* No limitations interfere with the creation of multi-sheet Excel files
The Windows only code, which calls COM to write the documents
should work very well. It's limitations are:
should work very well.
* Supports only 1 stream in the file
The cross-platform code at this moment has several limitations,
The cross-platform code at this moment has some limitations,
but should work for most documents. Some limitations are:
* Supports only 1 stream in the file
@ -52,11 +51,8 @@ type
{ Describes an OLE Document }
TOLEDocument = record
// Information about the streams
// All arrays here should have the same length
// Actually at the time all of them should have length 1
Streams: array of TMemoryStream;
StreamsNumSectors: array of Cardinal;
// Information about the document
Stream: TMemoryStream;
end;
@ -70,7 +66,9 @@ type
{$endif}
{ Information filled by the write routines for the helper routines }
FOLEDocument: TOLEDocument;
FNumStreams, FNumSATSectors, FNumStreamSectors, FNumTotalSectors: Cardinal;
FNumSATSectors, FNumStreamSectors, FNumTotalSectors: Cardinal;
FNumStreamShortSectors: Cardinal;
FUseShortSectors: Boolean;
{ Helper routines }
procedure WriteOLEHeader(AStream: TStream);
procedure WriteSectorAllocationTable(AStream: TStream);
@ -92,6 +90,7 @@ const
INT_OLE_SECTOR_SIZE = 512; // in bytes
INT_OLE_SECTOR_DWORD_SIZE = 512 div 4; // in dwords
INT_OLE_SHORT_SECTOR_SIZE = 64; // in bytes
INT_OLE_MIN_SIZE_FOR_STANDARD_STREAMS = 4096;
INT_OLE_DIR_ENTRY_TYPE_EMPTY = 0;
INT_OLE_DIR_ENTRY_TYPE_USER_STREAM = 2;
@ -138,7 +137,9 @@ begin
AStream.WriteWord(WordToLE($0003));
{ 28 2 Byte order identifier (4.2): FEH FFH = Little-Endian
FFH FEH = Big-Endian }
FFH FEH = Big-Endian
Real applications only use Little-Endian, so we follow that }
AStream.WriteByte($FE);
AStream.WriteByte($FF);
@ -191,38 +192,61 @@ begin
for i := 1 to 108 do AStream.WriteDWord($FFFFFFFF);
end;
{
The file is organized as following:
HEADER
SECTOR 0 - SAT
SECTOR 1 - Directory stream
SECTOR 2 - Short SAT
SECTOR 3 and on - User data
And this SAT will describe that.
This results in the following SecID array for the SAT:
Array indexes 0 1 2 3 ... N-1 N ...
SecID array 3 1 2 4 ... -2 -1 ...
As expected, sector 0 is marked with the special SAT SecID (3.1).
Sector 1 and all sectors starting with sector 5 are
not used (special Free SecID with value 1).
}
procedure TOLEStorage.WriteSectorAllocationTable(AStream: TStream);
var
i: Integer;
i, CurrentPos, NextSecID: Integer;
begin
{ Simple copy of an example OLE file
{ Example values:
00000200H FD FF FF FF FF FF FF FF FE FF FF FF 04 00 00 00
00000210H FE FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
And from now on only $FFFFFFFF covering $220 to $3FF
for a total of $400 - $220 bytes of $FF }
And after that only $FFFFFFFF until $400 }
AStream.WriteDWord(DWordToLE($FFFFFFFD));
AStream.WriteDWord($FFFFFFFF);
AStream.WriteDWord(DWordToLE($FFFFFFFE));
AStream.WriteDWord(DWordToLE($00000004));
AStream.WriteDWord(DWordToLE($FFFFFFFE));
AStream.WriteDWord($FFFFFFFF);
AStream.WriteDWord($FFFFFFFF);
AStream.WriteDWord($FFFFFFFF);
AStream.WriteDWord(DWordToLE($FFFFFFFD)); // SAT
AStream.WriteDWord($FFFFFFFF); // Empty
AStream.WriteDWord(DWordToLE($FFFFFFFE)); // Start and End of Short SAT
for i := 1 to ($400 - $220) do AStream.WriteByte($FF);
CurrentPos := $200 + 12;
{
This results in the following SecID array for the SAT:
// Now write the user data
Array indexes 0 1 2 3 4 5 ...
SecID array 3 1 2 4 -2 -1 ...
NextSecID := $00000004;
As expected, sector 0 is marked with the special SAT SecID (3.1).
Sector 1 and all sectors starting with sector 5 are
not used (special Free SecID with value 1). }
for i := 2 to FNumStreamSectors do
begin
AStream.WriteDWord(DWordToLE(NextSecID));
Inc(NextSecID);
CurrentPos := CurrentPos + 4;
end;
AStream.WriteDWord(DWordToLE($FFFFFFFE)); // End of user data
CurrentPos := CurrentPos + 4;
// Fill the rest of the sector with $FF
for i := 1 to ($400 - CurrentPos) do AStream.WriteByte($FF);
end;
{
@ -258,9 +282,6 @@ begin
Item #3 e #4
00000540H 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FF
Root Storage #5
00000640H 16 00 05 00 FF FF FF FF FF FF FF FF 01 00 00 00
64 2 Size of the used area of the character buffer of the name (not character count), including
the trailing zero character (e.g. 12 for a name with 5 characters: (5+1)2 = 12)
66 1 Type of the entry: 00H = Empty 03H = LockBytes (unknown)
@ -322,19 +343,16 @@ begin
Item #3 e #4
00000570H XX XX XX XX 00 00 00 00 00 00 00 00 00 00 00 00
Root Storage #5
00000670H XX XX XX XX 03 00 00 00 40 03 00 00 00 00 00 00
Book #6
000004F0H XX XX XX XX 00 00 00 00 3F 03 00 00 00 00 00 00
First 4 bytes still with the timestamp.
116 4 SecID of first sector or short-sector, if this entry refers to a stream (7.2.2), SecID of first
sector of the short-stream container stream (6.1), if this is the root storage entry, 0
otherwise
120 4 Total stream size in bytes, if this entry refers to a stream (7.2.2), total size of the short-
stream container stream (6.1), if this is the root storage entry, 0 otherwise
120 4 Total stream size in bytes, if this entry refers to a stream (7.2.2),
total size of the short-stream container stream (6.1),
if this is the root storage entry, 0 otherwise
124 4 Not used
}
@ -347,14 +365,20 @@ begin
end;
procedure TOLEStorage.WriteDirectoryStream(AStream: TStream);
var
FContainerSize: Cardinal;
begin
{ Size of the container stream }
FContainerSize := Ceil(FOLEDocument.Stream.Size / INT_OLE_SECTOR_SIZE) * INT_OLE_SECTOR_SIZE;
WriteDirectoryEntry(AStream, 'Root Entry'#0,
INT_OLE_DIR_ENTRY_TYPE_ROOT_STORAGE, INT_OLE_DIR_COLOR_RED,
True, $00000340);
True, FContainerSize);
WriteDirectoryEntry(AStream, 'Book'#0,
INT_OLE_DIR_ENTRY_TYPE_USER_STREAM, INT_OLE_DIR_COLOR_BLACK,
False, $0000033F);
False, FOLEDocument.Stream.Size);
WriteDirectoryEntry(AStream, #0,
INT_OLE_DIR_ENTRY_TYPE_EMPTY, INT_OLE_DIR_COLOR_RED,
@ -386,29 +410,22 @@ All short-sectors starting with sector 54 are not used (special Free SecID with
}
procedure TOLEStorage.WriteShortSectorAllocationTable(AStream: TStream);
var
i: Integer;
i, NextShortSecID, CurrentPos: Integer;
begin
AStream.WriteDWord(DWordToLE($00000001));
AStream.WriteDWord(DWordToLE($00000002));
AStream.WriteDWord(DWordToLE($00000003));
AStream.WriteDWord(DWordToLE($00000004));
CurrentPos := $800;
NextShortSecID := $00000001;
AStream.WriteDWord(DWordToLE($00000005));
AStream.WriteDWord(DWordToLE($00000006));
AStream.WriteDWord(DWordToLE($00000007));
AStream.WriteDWord(DWordToLE($00000008));
AStream.WriteDWord(DWordToLE($00000009));
AStream.WriteDWord(DWordToLE($0000000A));
AStream.WriteDWord(DWordToLE($0000000B));
AStream.WriteDWord(DWordToLE($0000000C));
for i := 2 to FNumStreamShortSectors do
begin
AStream.WriteDWord(DWordToLE(NextShortSecID));
Inc(NextShortSecID);
Inc(CurrentPos, 4);
end;
AStream.WriteDWord(DWordToLE($FFFFFFFE));
AStream.WriteDWord(DWordToLE($FFFFFFFF));
AStream.WriteDWord(DWordToLE($FFFFFFFF));
AStream.WriteDWord(DWordToLE($FFFFFFFF));
Inc(CurrentPos, 4);
for i := 1 to ($A00 - $840) do AStream.WriteByte($FF);
for i := 1 to ($A00 - CurrentPos) do AStream.WriteByte($FF);
end;
procedure TOLEStorage.WriteUserStream(ADest, ASource: TStream);
@ -443,23 +460,23 @@ var
begin
{ Fill information for helper routines }
FOLEDocument := AOLEDocument;
FNumStreams := Length(AOLEDocument.Streams);
{ Calculate the number of sectors necessary for each stream }
SetLength(FOLEDocument.StreamsNumSectors, FNumStreams);
{ Calculate the number of sectors necessary for the stream }
FNumStreamSectors := 0;
FNumStreamSectors := Ceil(AOLEDocument.Stream.Size / INT_OLE_SECTOR_SIZE);
for i := 0 to FNumStreams - 1 do
begin
x := Ceil(AOLEDocument.Streams[i].Size / INT_OLE_SECTOR_SIZE);
FOLEDocument.StreamsNumSectors[i] := x;
FNumStreamSectors := FNumStreamSectors + x;
end;
{ Calculates the number of short sectors for the stream, if it applies }
FUseShortSectors := AOLEDocument.Stream.Size < INT_OLE_MIN_SIZE_FOR_STANDARD_STREAMS;
FNumStreamShortSectors := Ceil(AOLEDocument.Stream.Size / INT_OLE_SHORT_SECTOR_SIZE);
{ Numbers of sectors necessary for the SAT }
FNumSATSectors := 1; // Ceil(FNumStreamSectors / INT_OLE_SECTOR_DWORD_SIZE);
{$ifdef FPOLESTORAGE_USE_COM}
{ Initialize the Component Object Model (COM) before calling s functions }
OleCheck(CoInitialize(nil));
@ -468,18 +485,17 @@ begin
STGM_READWRITE or STGM_FAILIFTHERE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT,
0, FStorage));
for i := 0 to FNumStreams - 1 do
begin
{ Create a workbook stream in the storage. A BIFF5 file must
have at least a workbook stream. This stream *must* be named 'Book' }
OleCheck(FStorage.CreateStream('Book',
STGM_READWRITE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT, 0, 0, FStream));
{ Create a workbook stream in the storage. A BIFF5 file must
have at least a workbook stream. This stream *must* be named 'Book' }
OleCheck(FStorage.CreateStream('Book',
STGM_READWRITE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT, 0, 0, FStream));
{ Write all data }
FStream.Write(FOLEDocument.Stream.Memory,
FOLEDocument.Stream.Size, @cbWritten);
{ Write all data }
FStream.Write(FOLEDocument.Streams[i].Memory,
FOLEDocument.Streams[i].Size, @cbWritten);
end;
{$else}
AFileStream := TFileStream.Create(AFileName, fmOpenWrite or fmCreate);
try
// Header
@ -495,10 +511,11 @@ begin
WriteShortSectorAllocationTable(AFileStream);
// Records 3 and on, the user data
WriteUserStream(AFileStream, FOLEDocument.Streams[0]);
WriteUserStream(AFileStream, FOLEDocument.Stream);
finally
AFileStream.Free;
end;
{$endif}
end;

View File

@ -213,16 +213,14 @@ begin
OutputStorage := TOLEStorage.Create;
try
WriteToStream(MemStream, AData);
SetLength(OLEDocument.Streams, 1);
OLEDocument.Streams[0] := MemStream;
// Only one stream is necessary for any number of worksheets
OLEDocument.Stream := MemStream;
OutputStorage.WriteOLEFile(AFileName, OLEDocument);
finally
MemStream.Free;
OutputStorage.Free;
SetLength(OLEDocument.Streams, 0);
end;
end;
@ -302,6 +300,7 @@ begin
WriteStyle(AStream);
// A BOUNDSHEET for each worksheet
for i := 0 to AData.GetWorksheetCount - 1 do
begin
len := Length(Boundsheets);