fpspreadsheet: Improved OLE storage code

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@651 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
sekelsenmat 2009-01-06 19:35:08 +00:00
parent a52fbe2039
commit 99057104e2
3 changed files with 375 additions and 73 deletions

View File

@ -38,7 +38,7 @@
<CursorPos X="16" Y="16"/>
<TopLine Value="1"/>
<EditorIndex Value="0"/>
<UsageCount Value="51"/>
<UsageCount Value="96"/>
<Loaded Value="True"/>
</Unit0>
<Unit1>
@ -46,61 +46,61 @@
<UnitName Value="fpspreadsheet"/>
<CursorPos X="32" Y="414"/>
<TopLine Value="388"/>
<UsageCount Value="25"/>
<UsageCount Value="20"/>
</Unit1>
<Unit2>
<Filename Value="..\xlsbiff5.pas"/>
<UnitName Value="xlsbiff5"/>
<CursorPos X="16" Y="320"/>
<TopLine Value="300"/>
<UsageCount Value="25"/>
<UsageCount Value="20"/>
</Unit2>
<Unit3>
<Filename Value="..\..\..\..\..\FPC220\source\packages\base\winunits\activex.pp"/>
<UnitName Value="ActiveX"/>
<CursorPos X="27" Y="29"/>
<TopLine Value="6"/>
<UsageCount Value="9"/>
<UsageCount Value="4"/>
</Unit3>
<Unit4>
<Filename Value="..\fpolestorage.pas"/>
<UnitName Value="fpolestorage"/>
<CursorPos X="1" Y="1"/>
<TopLine Value="1"/>
<UsageCount Value="45"/>
<UsageCount Value="40"/>
</Unit4>
<Unit5>
<Filename Value="..\..\..\..\..\FPC220\source\rtl\inc\objpash.inc"/>
<CursorPos X="21" Y="141"/>
<TopLine Value="131"/>
<UsageCount Value="9"/>
<UsageCount Value="4"/>
</Unit5>
<Unit6>
<Filename Value="..\xlsbiff2.pas"/>
<UnitName Value="xlsbiff2"/>
<CursorPos X="20" Y="277"/>
<TopLine Value="260"/>
<UsageCount Value="18"/>
<UsageCount Value="13"/>
</Unit6>
<Unit7>
<Filename Value="..\..\..\..\..\FPC220\source\rtl\objpas\classes\classesh.inc"/>
<CursorPos X="22" Y="1602"/>
<TopLine Value="1598"/>
<UsageCount Value="11"/>
<UsageCount Value="6"/>
</Unit7>
<Unit8>
<Filename Value="..\..\..\..\..\FPC220\source\rtl\win32\buildrtl.pp"/>
<UnitName Value="buildrtl"/>
<CursorPos X="29" Y="5"/>
<TopLine Value="1"/>
<UsageCount Value="9"/>
<UsageCount Value="4"/>
</Unit8>
<Unit9>
<Filename Value="..\..\..\..\..\FPC220\source\rtl\objpas\fgl.pp"/>
<UnitName Value="fgl"/>
<CursorPos X="15" Y="86"/>
<TopLine Value="55"/>
<UsageCount Value="18"/>
<UsageCount Value="13"/>
</Unit9>
<Unit10>
<Filename Value="..\..\..\..\..\lazarus\lcl\interfaces\win32\win32wsstdctrls.pp"/>
@ -108,7 +108,7 @@
<CursorPos X="11" Y="737"/>
<TopLine Value="713"/>
<EditorIndex Value="6"/>
<UsageCount Value="15"/>
<UsageCount Value="37"/>
<Loaded Value="True"/>
</Unit10>
<Unit11>
@ -116,14 +116,14 @@
<UnitName Value="Win32WSControls"/>
<CursorPos X="13" Y="206"/>
<TopLine Value="199"/>
<UsageCount Value="12"/>
<UsageCount Value="7"/>
</Unit11>
<Unit12>
<Filename Value="..\..\..\..\..\lazarus\components\sqlite\registersqlite3.pas"/>
<UnitName Value="registersqlite3"/>
<CursorPos X="15" Y="5"/>
<TopLine Value="1"/>
<UsageCount Value="11"/>
<UsageCount Value="6"/>
</Unit12>
<Unit13>
<Filename Value="..\..\..\..\..\lazarus\ideintf\componenteditors.pas"/>
@ -131,22 +131,22 @@
<CursorPos X="54" Y="353"/>
<TopLine Value="330"/>
<EditorIndex Value="5"/>
<UsageCount Value="13"/>
<UsageCount Value="35"/>
<Loaded Value="True"/>
</Unit13>
<Unit14>
<Filename Value="..\..\..\..\..\FPC220\source\rtl\objpas\classes\cregist.inc"/>
<CursorPos X="17" Y="124"/>
<TopLine Value="121"/>
<UsageCount Value="11"/>
<UsageCount Value="6"/>
</Unit14>
<Unit15>
<Filename Value="..\..\xlsbiff5.pas"/>
<UnitName Value="xlsbiff5"/>
<CursorPos X="41" Y="218"/>
<TopLine Value="209"/>
<CursorPos X="28" Y="62"/>
<TopLine Value="59"/>
<EditorIndex Value="2"/>
<UsageCount Value="11"/>
<UsageCount Value="33"/>
<Loaded Value="True"/>
</Unit15>
<Unit16>
@ -155,7 +155,7 @@
<CursorPos X="1" Y="49"/>
<TopLine Value="30"/>
<EditorIndex Value="1"/>
<UsageCount Value="11"/>
<UsageCount Value="33"/>
<Loaded Value="True"/>
</Unit16>
<Unit17>
@ -164,80 +164,140 @@
<CursorPos X="1" Y="69"/>
<TopLine Value="57"/>
<EditorIndex Value="3"/>
<UsageCount Value="11"/>
<UsageCount Value="33"/>
<Loaded Value="True"/>
</Unit17>
<Unit18>
<Filename Value="..\..\fpolestorage.pas"/>
<UnitName Value="fpolestorage"/>
<CursorPos X="33" Y="133"/>
<TopLine Value="121"/>
<CursorPos X="1" Y="471"/>
<TopLine Value="458"/>
<EditorIndex Value="4"/>
<UsageCount Value="11"/>
<UsageCount Value="33"/>
<Loaded Value="True"/>
</Unit18>
</Units>
<JumpHistory Count="15" HistoryIndex="14">
<JumpHistory Count="30" HistoryIndex="29">
<Position1>
<Filename Value="..\..\..\..\..\lazarus\lcl\interfaces\win32\win32wsstdctrls.pp"/>
<Caret Line="1" Column="1" TopLine="1"/>
<Caret Line="737" Column="11" TopLine="713"/>
</Position1>
<Position2>
<Filename Value="..\..\..\..\..\lazarus\lcl\interfaces\win32\win32wsstdctrls.pp"/>
<Caret Line="737" Column="11" TopLine="713"/>
</Position2>
<Position3>
<Filename Value="..\..\..\..\..\lazarus\ideintf\componenteditors.pas"/>
<Caret Line="353" Column="54" TopLine="330"/>
</Position3>
<Position4>
</Position2>
<Position3>
<Filename Value="excel5demo.lpr"/>
<Caret Line="49" Column="12" TopLine="30"/>
</Position4>
<Position5>
</Position3>
<Position4>
<Filename Value="..\..\xlsbiff5.pas"/>
<Caret Line="365" Column="37" TopLine="354"/>
</Position4>
<Position5>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="38" Column="20" TopLine="21"/>
</Position5>
<Position6>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="38" Column="20" TopLine="21"/>
<Caret Line="60" Column="54" TopLine="49"/>
</Position6>
<Position7>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="60" Column="54" TopLine="49"/>
<Caret Line="44" Column="24" TopLine="32"/>
</Position7>
<Position8>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="44" Column="24" TopLine="32"/>
<Caret Line="51" Column="25" TopLine="34"/>
</Position8>
<Position9>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="51" Column="25" TopLine="34"/>
<Caret Line="157" Column="21" TopLine="151"/>
</Position9>
<Position10>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="157" Column="21" TopLine="151"/>
<Caret Line="155" Column="5" TopLine="136"/>
</Position10>
<Position11>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="155" Column="5" TopLine="136"/>
<Caret Line="65" Column="3" TopLine="58"/>
</Position11>
<Position12>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="65" Column="3" TopLine="58"/>
<Caret Line="25" Column="11" TopLine="16"/>
</Position12>
<Position13>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="25" Column="11" TopLine="16"/>
</Position13>
<Position14>
<Filename Value="..\..\xlsbiff5.pas"/>
<Caret Line="194" Column="15" TopLine="60"/>
</Position14>
<Position15>
</Position13>
<Position14>
<Filename Value="..\..\fpsutils.pas"/>
<Caret Line="45" Column="5" TopLine="26"/>
</Position14>
<Position15>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="150" Column="5" TopLine="131"/>
</Position15>
<Position16>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="191" Column="5" TopLine="172"/>
</Position16>
<Position17>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="49" Column="15" TopLine="39"/>
</Position17>
<Position18>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="331" Column="45" TopLine="326"/>
</Position18>
<Position19>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="24" Column="27" TopLine="19"/>
</Position19>
<Position20>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="247" Column="1" TopLine="229"/>
</Position20>
<Position21>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="224" Column="5" TopLine="205"/>
</Position21>
<Position22>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="231" Column="1" TopLine="219"/>
</Position22>
<Position23>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="377" Column="5" TopLine="358"/>
</Position23>
<Position24>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="225" Column="1" TopLine="211"/>
</Position24>
<Position25>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="246" Column="12" TopLine="237"/>
</Position25>
<Position26>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="79" Column="16" TopLine="78"/>
</Position26>
<Position27>
<Filename Value="..\..\xlsbiff5.pas"/>
<Caret Line="87" Column="16" TopLine="77"/>
</Position27>
<Position28>
<Filename Value="..\..\xlsbiff5.pas"/>
<Caret Line="218" Column="24" TopLine="207"/>
</Position28>
<Position29>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="235" Column="30" TopLine="224"/>
</Position29>
<Position30>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="80" Column="28" TopLine="68"/>
</Position30>
</JumpHistory>
</ProjectOptions>
<CompilerOptions>

View File

@ -4,6 +4,29 @@ fpolestorage.pas
Writes an OLE document
AUTHORS: Felipe Monteiro de Carvalho
Limitations of this unit for creating OLE documents:
* Can only create documents with an array of streams. It's not possible
to create real directory structures like the OLE format supports.
This is no problem for most applications.
The Windows only code, which calls COM to write the documents
should work very well. It's limitations are:
* Supports only 1 stream in the file
The cross-platform code at this moment has several limitations,
but should work for most documents. Some limitations are:
* Supports only 1 stream in the file
* Fixed sectors size of 512 bytes
* Fixed short sector size of 64 bytes
* Never allocates more space for the MSAT, limiting the SAT to 109 sectors,
which means a total
* Never allocates more then 1 sector for the SAT, so the document may have
only up to 512 / 4 = 128 sectors
}
unit fpolestorage;
@ -21,7 +44,7 @@ uses
{$ifdef FPOLESTORAGE_USE_COM}
ActiveX, ComObj,
{$endif}
Classes, SysUtils,
Classes, SysUtils, Math,
fpsutils;
type
@ -29,7 +52,11 @@ type
{ Describes an OLE Document }
TOLEDocument = record
Sections: array of TMemoryStream;
// 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;
end;
@ -43,10 +70,15 @@ type
{$endif}
{ Information filled by the write routines for the helper routines }
FOLEDocument: TOLEDocument;
FNumSectors: Cardinal;
FNumStreams, FNumSATSectors, FNumStreamSectors, FNumTotalSectors: Cardinal;
{ Helper routines }
procedure WriteOLEHeader(AStream: TStream);
procedure WriteSectorAllocationTable(AStream: TStream);
procedure WriteDirectoryStream(AStream: TStream);
procedure WriteDirectoryEntry(AStream: TStream; AName: widestring;
EntryType, EntryColor: Byte; AIsStorage: Boolean;
AStreamSize: Cardinal);
procedure WriteShortSectorAllocationTable(AStream: TStream);
public
constructor Create;
destructor Destroy; override;
@ -55,6 +87,18 @@ type
implementation
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_DIR_ENTRY_TYPE_EMPTY = 0;
INT_OLE_DIR_ENTRY_TYPE_USER_STREAM = 2;
INT_OLE_DIR_ENTRY_TYPE_ROOT_STORAGE = 5;
INT_OLE_DIR_COLOR_RED = 0;
INT_OLE_DIR_COLOR_BLACK = 1;
{ TOLEStorage }
{
@ -111,7 +155,7 @@ begin
AStream.WriteWord($0);
{ 44 4 Total number of sectors used for the sector allocation table (➜5.2) }
AStream.WriteDWord(DWordToLE(FNumSectors));
AStream.WriteDWord(DWordToLE(1));
{ 48 4 SecID of first sector of the directory stream (➜7) }
AStream.WriteDWord(DWordToLE($01));
@ -151,37 +195,207 @@ begin
{ Simple copy of an example OLE file
00000200H FD FF FF FF FF FF FF FF FE FF FF FF 04 00 00 00
00000210H 05 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00
00000220H 09 00 00 00 FE FF FF FF 0B 00 00 00 FE FF FF FF
00000210H FE FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
And from now on only $FFFFFFFF covering $230 to $3FF
for a total of $400 - $230 bytes of $FF }
And from now on only $FFFFFFFF covering $220 to $3FF
for a total of $400 - $220 bytes of $FF }
AStream.WriteDWord(DWordToLE($FFFFFFFD));
AStream.WriteDWord($FFFFFFFF);
AStream.WriteDWord(DWordToLE($FFFFFFFE));
AStream.WriteDWord(DWordToLE($00000004));
AStream.WriteDWord(DWordToLE($00000005));
AStream.WriteDWord(DWordToLE($00000006));
AStream.WriteDWord(DWordToLE($00000007));
AStream.WriteDWord(DWordToLE($00000008));
AStream.WriteDWord(DWordToLE($00000009));
AStream.WriteDWord(DWordToLE($FFFFFFFE));
AStream.WriteDWord(DWordToLE($0000000B));
AStream.WriteDWord(DWordToLE($FFFFFFFE));
AStream.WriteDWord($FFFFFFFF);
AStream.WriteDWord($FFFFFFFF);
AStream.WriteDWord($FFFFFFFF);
for i := 1 to ($400 - $230) do AStream.WriteByte($FF);
for i := 1 to ($400 - $220) do AStream.WriteByte($FF);
{
This results in the following SecID array for the SAT:
Array indexes 0 1 2 3 4 5 6 7 8 9 10 11 12 ...
SecID array 3 1 2 4 5 6 7 8 9 2 11 2 1 ...
Array indexes 0 1 2 3 4 5 ...
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 12 are
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). }
end;
{
7.2.1 Directory Entry Structure
The size of each directory entry is exactly 128 bytes. The formula to calculate an offset in the directory stream from a
DirID is as follows:
dir_entry_pos(DirID) = DirID 128
}
procedure TOLEStorage.WriteDirectoryEntry(AStream: TStream; AName: widestring;
EntryType, EntryColor: Byte; AIsStorage: Boolean;
AStreamSize: Cardinal);
var
i: Integer;
EntryName: array[0..31] of WideChar;
begin
{ Contents of the directory entry structure:
Offset Size Contents
0 64 Character array of the name of the entry, always 16-bit Unicode characters, with trailing
zero character (results in a maximum name length of 31 characters)
00000400H 52 00 6F 00 6F 00 74 00 20 00 45 00 6E 00 74 00 }
EntryName := AName;
AStream.WriteBuffer(EntryName, 64);
{Root Storage #1
00000440H 16 00 05 00 FF FF FF FF FF FF FF FF 01 00 00 00
Book #2
000004C0H 0A 00 02 01 FF FF FF FF FF FF FF FF FF FF FF FF
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)
01H = User storage 04H = Property (unknown)
02H = User stream 05H = Root storage
67 1 Node colour of the entry: 00H = Red 01H = Black
68 4 DirID of the left child node inside the red-black tree of all direct members of the parent
storage (if this entry is a user storage or stream, 7.1), 1 if there is no left child
72 4 DirID of the right child node inside the red-black tree of all direct members of the parent
storage (if this entry is a user storage or stream, 7.1), 1 if there is no right child
76 4 DirID of the root node entry of the red-black tree of all storage members (if this entry is a
storage, 7.1), 1 otherwise
}
AStream.WriteWord(WordToLE(Length(AName) * 2));
AStream.WriteByte(EntryType);
AStream.WriteByte(EntryColor);
AStream.WriteDWord(DWordToLE($FFFFFFFF));
AStream.WriteDWord(DWordToLE($FFFFFFFF));
if AIsStorage then AStream.WriteDWord(DWordToLE($00000001))
else AStream.WriteDWord(DWordToLE($FFFFFFFF));;
{00000450H 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
80 16 Unique identifier, if this is a storage (not of interest in the following, may be all 0)
00000460H 00 00 00 00 00 00 00 00 00 00 00 00 00 4E 67 0E
00000470H 39 6F C9 01
96 4 User flags (not of interest in the following, may be all 0)
100 8 Time stamp of creation of this entry (7.2.3). Most implementations do not write a valid
time stamp, but fill up this space with zero bytes.
108 8 Time stamp of last modification of this entry (7.2.3). Most implementations do not write
a valid time stamp, but fill up this space with zero bytes.
}
for i := 1 to ($474 - $450) do AStream.WriteByte($00);
{Root Storage #1
00000470H XX XX XX XX 03 00 00 00 40 03 00 00 00 00 00 00
Book #2
000004F0H XX XX XX XX 00 00 00 00 3F 03 00 00 00 00 00 00
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
124 4 Not used
}
if AIsStorage then AStream.WriteDWord(DWordToLE($00000003))
else AStream.WriteDWord(0);
AStream.WriteDWord(DWordToLE(AStreamSize));
AStream.WriteDWord(DWordToLE($00000000));
end;
procedure TOLEStorage.WriteDirectoryStream(AStream: TStream);
begin
WriteDirectoryEntry(AStream, 'Root Entry'#0,
INT_OLE_DIR_ENTRY_TYPE_ROOT_STORAGE, INT_OLE_DIR_COLOR_RED,
True, $00000340);
WriteDirectoryEntry(AStream, 'Book'#0,
INT_OLE_DIR_ENTRY_TYPE_USER_STREAM, INT_OLE_DIR_COLOR_BLACK,
False, $0000033F);
WriteDirectoryEntry(AStream, #0,
INT_OLE_DIR_ENTRY_TYPE_EMPTY, INT_OLE_DIR_COLOR_RED,
False, $00000000);
WriteDirectoryEntry(AStream, #0,
INT_OLE_DIR_ENTRY_TYPE_EMPTY, INT_OLE_DIR_COLOR_RED,
False, $00000000);
end;
{
8.4 Short-Sector Allocation Table
The short-sector allocation table (SSAT) is an array of SecIDs and contains the SecID chains (3.2) of all short-
streams, similar to the sector allocation table (5.2) that contains the SecID chains of standard streams.
The first SecID of the SSAT is contained in the header (4.1), the remaining SecID chain is contained in the SAT. The
SSAT is built by reading and concatenating the contents of all sectors.
Contents of a sector of the SSAT (sec_size is the size of a sector in bytes, see 4.1):
Offset Size Contents
0 sec_size Array of sec_size/4 SecIDs of the SSAT
The SSAT will be used similarly to the SAT (5.2) with the difference that the SecID chains refer to short-sectors in the
short-stream container stream (6.1).
This results in the following SecID array for the SSAT:
Array
indexes 0 1 2 3 4 5 6 7 8 9 1011...4142434445464748495051525354...
SecID array 1 2 3 4 5 6 7 8 9 101112...42434445247225051525321...
All short-sectors starting with sector 54 are not used (special Free SecID with value 1).
}
procedure TOLEStorage.WriteShortSectorAllocationTable(AStream: TStream);
var
i: Integer;
begin
AStream.WriteDWord(DWordToLE($00000001));
AStream.WriteDWord(DWordToLE($00000002));
AStream.WriteDWord(DWordToLE($00000003));
AStream.WriteDWord(DWordToLE($00000004));
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));
AStream.WriteDWord(DWordToLE($FFFFFFFE));
AStream.WriteDWord(DWordToLE($FFFFFFFF));
AStream.WriteDWord(DWordToLE($FFFFFFFF));
AStream.WriteDWord(DWordToLE($FFFFFFFF));
for i := 1 to ($A00 - $840) do AStream.WriteByte($FF);
end;
constructor TOLEStorage.Create;
begin
inherited Create;
@ -198,11 +412,25 @@ procedure TOLEStorage.WriteOLEFile(AFileName: string; AOLEDocument: TOLEDocument
var
cbWritten: Cardinal;
AFileStream: TFileStream;
i: Cardinal;
i, x: Cardinal;
begin
{ Fill information for helper routines }
FOLEDocument := AOLEDocument;
FNumSectors := Length(AOLEDocument.Sections);
FNumStreams := Length(AOLEDocument.Streams);
{ Calculate the number of sectors necessary for each stream }
SetLength(FOLEDocument.StreamsNumSectors, FNumStreams);
FNumStreamSectors := 0;
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;
FNumSATSectors := 1; // Ceil(FNumStreamSectors / INT_OLE_SECTOR_DWORD_SIZE);
{$ifdef FPOLESTORAGE_USE_COM}
{ Initialize the Component Object Model (COM) before calling s functions }
@ -213,7 +441,7 @@ begin
STGM_READWRITE or STGM_FAILIFTHERE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT,
0, FStorage));
for i := 0 to FNumSectors do
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' }
@ -221,13 +449,27 @@ begin
STGM_READWRITE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT, 0, 0, FStream));
{ Write all data }
FStream.Write(FOLEDocument.Sections[i].Memory,
FOLEDocument.Sections[i].Size, @cbWritten);
FStream.Write(FOLEDocument.Streams[i].Memory,
FOLEDocument.Streams[i].Size, @cbWritten);
end;
{$else}
AFileStream := TFileStream.Create(AFileName, fmOpenWrite or fmCreate);
try
// Header
WriteOLEHeader(AFileStream);
// Record 0, the SAT
WriteSectorAllocationTable(AFileStream);
// Records 1 and 2, the directory stream
WriteDirectoryStream(AFileStream);
WriteDirectoryStream(AFileStream);
// Record 3, the Short SAT
WriteShortSectorAllocationTable(AFileStream);
// Records 4 and on, the user data
AFileStream.CopyFrom(FOLEDocument.Streams[0]);
finally
AFileStream.Free;
end;

View File

@ -214,15 +214,15 @@ begin
try
WriteToStream(MemStream, AData);
SetLength(OLEDocument.Sections, 1);
OLEDocument.Sections[0] := MemStream;
SetLength(OLEDocument.Streams, 1);
OLEDocument.Streams[0] := MemStream;
OutputStorage.WriteOLEFile(AFileName, OLEDocument);
finally
MemStream.Free;
OutputStorage.Free;
SetLength(OLEDocument.Sections, 0);
SetLength(OLEDocument.Streams, 0);
end;
end;