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:
parent
d14bd9024d
commit
a52fbe2039
@ -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>
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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">
|
||||
|
BIN
components/fpspreadsheet/reference/compdocfileformat.pdf
Normal file
BIN
components/fpspreadsheet/reference/compdocfileformat.pdf
Normal file
Binary file not shown.
@ -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 }
|
||||
|
||||
{*******************************************************************
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user