Improved ole storage and adds reference documents

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@650 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
sekelsenmat 2009-01-05 14:43:39 +00:00
parent d14bd9024d
commit a52fbe2039
6 changed files with 276 additions and 60 deletions

View File

@ -6,7 +6,7 @@
<General>
<MainUnit Value="0"/>
<TargetFileExt Value=".exe"/>
<ActiveEditorIndexAtStart Value="0"/>
<ActiveEditorIndexAtStart Value="4"/>
</General>
<VersionInfo>
<ProjectVersion Value=""/>
@ -30,15 +30,15 @@
<PackageName Value="laz_fpspreadsheet"/>
</Item1>
</RequiredPackages>
<Units Count="15">
<Units Count="19">
<Unit0>
<Filename Value="excel5demo.lpr"/>
<IsPartOfProject Value="True"/>
<UnitName Value="excel5demo"/>
<CursorPos X="64" Y="13"/>
<TopLine Value="3"/>
<CursorPos X="16" Y="16"/>
<TopLine Value="1"/>
<EditorIndex Value="0"/>
<UsageCount Value="50"/>
<UsageCount Value="51"/>
<Loaded Value="True"/>
</Unit0>
<Unit1>
@ -107,8 +107,8 @@
<UnitName Value="Win32WSStdCtrls"/>
<CursorPos X="11" Y="737"/>
<TopLine Value="713"/>
<EditorIndex Value="2"/>
<UsageCount Value="14"/>
<EditorIndex Value="6"/>
<UsageCount Value="15"/>
<Loaded Value="True"/>
</Unit10>
<Unit11>
@ -130,8 +130,8 @@
<UnitName Value="ComponentEditors"/>
<CursorPos X="54" Y="353"/>
<TopLine Value="330"/>
<EditorIndex Value="1"/>
<UsageCount Value="12"/>
<EditorIndex Value="5"/>
<UsageCount Value="13"/>
<Loaded Value="True"/>
</Unit13>
<Unit14>
@ -140,8 +140,44 @@
<TopLine Value="121"/>
<UsageCount Value="11"/>
</Unit14>
<Unit15>
<Filename Value="..\..\xlsbiff5.pas"/>
<UnitName Value="xlsbiff5"/>
<CursorPos X="41" Y="218"/>
<TopLine Value="209"/>
<EditorIndex Value="2"/>
<UsageCount Value="11"/>
<Loaded Value="True"/>
</Unit15>
<Unit16>
<Filename Value="..\..\fpsutils.pas"/>
<UnitName Value="fpsutils"/>
<CursorPos X="1" Y="49"/>
<TopLine Value="30"/>
<EditorIndex Value="1"/>
<UsageCount Value="11"/>
<Loaded Value="True"/>
</Unit16>
<Unit17>
<Filename Value="..\..\xlsbiff2.pas"/>
<UnitName Value="xlsbiff2"/>
<CursorPos X="1" Y="69"/>
<TopLine Value="57"/>
<EditorIndex Value="3"/>
<UsageCount Value="11"/>
<Loaded Value="True"/>
</Unit17>
<Unit18>
<Filename Value="..\..\fpolestorage.pas"/>
<UnitName Value="fpolestorage"/>
<CursorPos X="33" Y="133"/>
<TopLine Value="121"/>
<EditorIndex Value="4"/>
<UsageCount Value="11"/>
<Loaded Value="True"/>
</Unit18>
</Units>
<JumpHistory Count="4" HistoryIndex="3">
<JumpHistory Count="15" HistoryIndex="14">
<Position1>
<Filename Value="..\..\..\..\..\lazarus\lcl\interfaces\win32\win32wsstdctrls.pp"/>
<Caret Line="1" Column="1" TopLine="1"/>
@ -158,6 +194,50 @@
<Filename Value="excel5demo.lpr"/>
<Caret Line="49" Column="12" TopLine="30"/>
</Position4>
<Position5>
<Filename Value="..\..\xlsbiff5.pas"/>
<Caret Line="365" Column="37" TopLine="354"/>
</Position5>
<Position6>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="38" Column="20" TopLine="21"/>
</Position6>
<Position7>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="60" Column="54" TopLine="49"/>
</Position7>
<Position8>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="44" Column="24" TopLine="32"/>
</Position8>
<Position9>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="51" Column="25" TopLine="34"/>
</Position9>
<Position10>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="157" Column="21" TopLine="151"/>
</Position10>
<Position11>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="155" Column="5" TopLine="136"/>
</Position11>
<Position12>
<Filename Value="..\..\fpolestorage.pas"/>
<Caret Line="65" Column="3" TopLine="58"/>
</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>
<Filename Value="..\..\fpsutils.pas"/>
<Caret Line="45" Column="5" TopLine="26"/>
</Position15>
</JumpHistory>
</ProjectOptions>
<CompilerOptions>

View File

@ -13,32 +13,175 @@ unit fpolestorage;
interface
uses
{$ifdef Windows}
{$define FPOLESTORAGE_USE_COM}
{$endif}
uses
{$ifdef FPOLESTORAGE_USE_COM}
ActiveX, ComObj,
{$endif}
Classes, SysUtils;
Classes, SysUtils,
fpsutils;
type
{ Describes an OLE Document }
TOLEDocument = record
Sections: array of TMemoryStream;
end;
{ TOLEStorage }
TOLEStorage = class
private
{$ifdef Windows}
{$ifdef FPOLESTORAGE_USE_COM}
FStorage: IStorage;
FStream: IStream;
{$endif}
{ Information filled by the write routines for the helper routines }
FOLEDocument: TOLEDocument;
FNumSectors: Cardinal;
{ Helper routines }
procedure WriteOLEHeader(AStream: TStream);
procedure WriteSectorAllocationTable(AStream: TStream);
public
constructor Create;
destructor Destroy; override;
procedure WriteStreamToOLEFile(AFileName: string; AMemStream: TMemoryStream);
procedure WriteOLEFile(AFileName: string; AOLEDocument: TOLEDocument);
end;
implementation
{ TOLEStorage }
{
4.1 Compound Document Header Contents
The header is always located at the beginning of the file, and its size is exactly 512 bytes. This implies that the first
sector (with SecID 0) always starts at file offset 512.
}
procedure TOLEStorage.WriteOLEHeader(AStream: TStream);
var
i: Integer;
begin
{
Contents of the compound document header structure:
Offset Size Contents
0 8 Compound document file identifier: D0H CFH 11H E0H A1H B1H 1AH E1H
}
AStream.WriteByte($D0);
AStream.WriteByte($CF);
AStream.WriteByte($11);
AStream.WriteByte($E0);
AStream.WriteByte($A1);
AStream.WriteByte($B1);
AStream.WriteByte($1A);
AStream.WriteByte($E1);
{ 8 16 Unique identifier (UID) of this file (not of interest in the following, may be all 0) }
AStream.WriteDWord(0);
AStream.WriteDWord(0);
{ 24 2 Revision number of the file format (most used is 003EH) }
AStream.WriteWord(WordToLE($003E));
{ 26 2 Version number of the file format (most used is 0003H) }
AStream.WriteWord(WordToLE($0003));
{ 28 2 Byte order identifier (4.2): FEH FFH = Little-Endian
FFH FEH = Big-Endian }
AStream.WriteByte($FE);
AStream.WriteByte($FF);
{ 30 2 Size of a sector in the compound document file (3.1) in power-of-two (ssz), real sector
size is sec_size = 2ssz bytes (minimum value is 7 which means 128 bytes, most used
value is 9 which means 512 bytes) }
AStream.WriteWord(WordToLE($0009));
{ 32 2 Size of a short-sector in the short-stream container stream (6.1) in power-of-two (sssz),
real short-sector size is short_sec_size = 2sssz bytes (maximum value is sector size
ssz, see above, most used value is 6 which means 64 bytes) }
AStream.WriteWord(WordToLE($0006));
{ 34 10 Not used }
AStream.WriteDWord($0);
AStream.WriteDWord($0);
AStream.WriteWord($0);
{ 44 4 Total number of sectors used for the sector allocation table (➜5.2) }
AStream.WriteDWord(DWordToLE(FNumSectors));
{ 48 4 SecID of first sector of the directory stream (➜7) }
AStream.WriteDWord(DWordToLE($01));
{ 52 4 Not used }
AStream.WriteDWord($0);
{ 56 4 Minimum size of a standard stream (in bytes, minimum allowed and most used size is 4096
bytes), streams with an actual size smaller than (and not equal to) this value are stored as
short-streams (6) }
AStream.WriteDWord(DWordToLE(4096));
{ 60 4 SecID of first sector of the short-sector allocation table (6.2), or 2 (End Of Chain
SecID, 3.1) if not extant }
AStream.WriteDWord(DWordToLE(2));
{ 64 4 Total number of sectors used for the short-sector allocation table (➜6.2) }
AStream.WriteDWord(DWordToLE(1));
{ 68 4 SecID of first sector of the master sector allocation table (5.1), or 2 (End Of Chain
SecID, 3.1) if no additional sectors used }
AStream.WriteDWord(IntegerToLE(-2));
{ 72 4 Total number of sectors used for the master sector allocation table (➜5.1) }
AStream.WriteDWord(0);
{ 76 436 First part of the master sector allocation table (➜5.1) containing 109 SecIDs }
AStream.WriteDWord(0);
for i := 1 to 108 do AStream.WriteDWord($FFFFFFFF);
end;
procedure TOLEStorage.WriteSectorAllocationTable(AStream: TStream);
var
i: Integer;
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
And from now on only $FFFFFFFF covering $230 to $3FF
for a total of $400 - $230 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));
for i := 1 to ($400 - $230) 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 ...
As expected, sector 0 is marked with the special SAT SecID (3.1). Sector 1 and all sectors starting with sector 12 are
not used (special Free SecID with value 1). }
end;
constructor TOLEStorage.Create;
begin
inherited Create;
@ -51,11 +194,17 @@ begin
inherited Destroy;
end;
procedure TOLEStorage.WriteStreamToOLEFile(AFileName: string; AMemStream: TMemoryStream);
procedure TOLEStorage.WriteOLEFile(AFileName: string; AOLEDocument: TOLEDocument);
var
cbWritten: Cardinal;
AFileStream: TFileStream;
i: Cardinal;
begin
{$ifdef Windows}
{ Fill information for helper routines }
FOLEDocument := AOLEDocument;
FNumSectors := Length(AOLEDocument.Sections);
{$ifdef FPOLESTORAGE_USE_COM}
{ Initialize the Component Object Model (COM) before calling s functions }
OleCheck(CoInitialize(nil));
@ -64,13 +213,24 @@ begin
STGM_READWRITE or STGM_FAILIFTHERE or STGM_SHARE_EXCLUSIVE or STGM_DIRECT,
0, FStorage));
{ 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));
for i := 0 to FNumSectors 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));
{ Write all data }
FStream.Write(AMemStream.Memory, AMemStream.Size, @cbWritten);
{ Write all data }
FStream.Write(FOLEDocument.Sections[i].Memory,
FOLEDocument.Sections[i].Size, @cbWritten);
end;
{$else}
AFileStream := TFileStream.Create(AFileName, fmOpenWrite or fmCreate);
try
WriteOLEHeader(AFileStream);
finally
AFileStream.Free;
end;
{$endif}
end;

View File

@ -14,7 +14,7 @@
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions>
<Files Count="8">
<Files Count="9">
<Item1>
<Filename Value="fpolestorage.pas"/>
<UnitName Value="fpolestorage"/>
@ -47,6 +47,10 @@
<Filename Value="xlsxooxml.pas"/>
<UnitName Value="xlsxooxml"/>
</Item8>
<Item9>
<Filename Value="fpsutils.pas"/>
<UnitName Value="fpsutils"/>
</Item9>
</Files>
<Type Value="RunAndDesignTime"/>
<RequiredPkgs Count="1">

View File

@ -29,7 +29,7 @@ interface
uses
Classes, SysUtils,
fpspreadsheet;
fpspreadsheet, fpsutils;
type
@ -67,23 +67,6 @@ const
INT_EXCEL_CHART = $0020;
INT_EXCEL_MACRO_SHEET = $0040;
{
Endianess helper functions
Excel files are all written with Little Endian byte order,
so it's necessary to swap the data to be able to build a
correct file on big endian systems.
}
function WordToLE(AValue: Word): Word;
begin
{$IFDEF BIG_ENDIAN}
Result := ((AValue shl 8) and $FF00) or ((AValue shr 8) and $00FF);
{$ELSE}
Result := AValue;
{$ENDIF}
end;
{ TsSpreadBIFF2Writer }
{*******************************************************************

View File

@ -55,7 +55,7 @@ interface
uses
Classes, SysUtils, fpcanvas,
fpspreadsheet, fpolestorage;
fpspreadsheet, fpolestorage, fpsutils;
type
@ -182,23 +182,6 @@ const
MASK_XF_VERT_ALIGN = $70;
{
Endianess helper functions
Excel files are all written with Little Endian byte order,
so it's necessary to swap the data to be able to build a
correct file on big endian systems.
}
function WordToLE(AValue: Word): Word;
begin
{$IFDEF BIG_ENDIAN}
Result := ((AValue shl 8) and $FF00) or ((AValue shr 8) and $00FF);
{$ELSE}
Result := AValue;
{$ENDIF}
end;
{
Exported functions
}
@ -224,16 +207,22 @@ procedure TsSpreadBIFF5Writer.WriteToFile(AFileName: string; AData: TsWorkbook);
var
MemStream: TMemoryStream;
OutputStorage: TOLEStorage;
OLEDocument: TOLEDocument;
begin
MemStream := TMemoryStream.Create;
OutputStorage := TOLEStorage.Create;
try
WriteToStream(MemStream, AData);
OutputStorage.WriteStreamToOLEFile(AFileName, MemStream);
SetLength(OLEDocument.Sections, 1);
OLEDocument.Sections[0] := MemStream;
OutputStorage.WriteOLEFile(AFileName, OLEDocument);
finally
MemStream.Free;
OutputStorage.Free;
SetLength(OLEDocument.Sections, 0);
end;
end;