git-svn-id: https://192.168.0.254/svn/Componentes.Terceros.DevExpressVCL@38 05c56307-c608-d34a-929d-697000501d7a
2989 lines
90 KiB
ObjectPascal
2989 lines
90 KiB
ObjectPascal
|
|
{*******************************************************************}
|
|
{ }
|
|
{ Developer Express Cross platform Visual Component Library }
|
|
{ ExpressSpreadSheet }
|
|
{ }
|
|
{ Copyright (c) 2001-2009 Developer Express Inc. }
|
|
{ ALL RIGHTS RESERVED }
|
|
{ }
|
|
{ The entire contents of this file is protected by U.S. and }
|
|
{ International Copyright Laws. Unauthorized reproduction, }
|
|
{ reverse-engineering, and distribution of all or any portion of }
|
|
{ the code contained in this file is strictly prohibited and may }
|
|
{ result in severe civil and criminal penalties and will be }
|
|
{ prosecuted to the maximum extent possible under the law. }
|
|
{ }
|
|
{ RESTRICTIONS }
|
|
{ }
|
|
{ THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES }
|
|
{ (DCU, OBJ, DLL, ETC.) ARE CONFIDENTIAL AND PROPRIETARY TRADE }
|
|
{ SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS }
|
|
{ LICENSED TO DISTRIBUTE THE EXPRESSSPREADSHEET AND ALL }
|
|
{ ACCOMPANYING VCL AND CLX CONTROLS AS PART OF AN EXECUTABLE }
|
|
{ PROGRAM ONLY. }
|
|
{ }
|
|
{ THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED }
|
|
{ FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE }
|
|
{ COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE }
|
|
{ AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT }
|
|
{ AND PERMISSION FROM DEVELOPER EXPRESS INC. }
|
|
{ }
|
|
{ CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON }
|
|
{ ADDITIONAL RESTRICTIONS. }
|
|
{ }
|
|
{*******************************************************************}
|
|
unit cxExcelAccess;
|
|
|
|
{$I cxVer.inc}
|
|
|
|
interface
|
|
uses
|
|
Classes, SysUtils, Math,
|
|
{$IFDEF DELPHI6} Variants, {$ENDIF} Windows,
|
|
Graphics, Dialogs, cxSSTypes, cxClasses, cxControls,
|
|
cxSSUtils, cxExcelConst, cxSSIntf, cxSSRes, dxCore;
|
|
|
|
type
|
|
EcxExcelDataReader = class(EdxException);
|
|
EcxExcelDataWriter = class(EdxException);
|
|
|
|
TSSTStringTable = class;
|
|
|
|
TcxStringArray = array of string;
|
|
TcxProgressEvent = procedure (Sender: TObject; APercent: Byte) of object;
|
|
|
|
{ TcxExcelFileReader }
|
|
TcxExcelFileReader = class
|
|
private
|
|
FBoundSheets: TStringList;
|
|
FCurrentSheet: IcxBookSheet;
|
|
FCurrentPage: Integer;
|
|
FExtRecordSize: Integer;
|
|
FFontArray: array of TcxSSFontRec;
|
|
FFuncConverter: TObject;
|
|
FHasUnknownFunction: Boolean;
|
|
FNames: TcxStringArray;
|
|
FOnProgress: TcxProgressEvent;
|
|
FOwner: IcxSpreadSheetBook;
|
|
FPixelsPerInch: Integer;
|
|
FProgress: Integer;
|
|
FReader: TObject;
|
|
FSharedStringTable: TStringList;
|
|
FStyleList: TStringList;
|
|
FSheetXlt: array of Word;
|
|
FStandardColWidth: Integer;
|
|
FXFRecords: array of TcxSSCellStyleRec;
|
|
function ConvertBrushStyles(ABrushStyle: Byte): TcxSSFillStyle;
|
|
function GetDataSize: Integer;
|
|
function GetMemoryData: Pointer;
|
|
protected
|
|
procedure DoReadRecords; virtual;
|
|
procedure DoReadUnknownRecord(var ARec: TBiffRecHeader); virtual;
|
|
procedure ProgressUpdate(APos: Pointer);
|
|
function XltPage(const APage: Word): Word;
|
|
property BoundSheets: TStringList read FBoundSheets;
|
|
property CurrentSheet: IcxBookSheet read FCurrentSheet;
|
|
property DataSize: Integer read GetDataSize;
|
|
property MemoryData: Pointer read GetMemoryData;
|
|
property Names: TcxStringArray read FNames;
|
|
property Owner: IcxSpreadSheetBook read FOwner;
|
|
property StandardColWidth: Integer read FStandardColWidth;
|
|
property Styles: TStringList read FStyleList;
|
|
public
|
|
constructor Create(AOwner: TObject); virtual;
|
|
destructor Destroy; override;
|
|
function AssignStream(AStream: TStream): Boolean;
|
|
procedure OpenStream;
|
|
property PixelsPerInch: Integer read FPixelsPerInch write FPixelsPerInch;
|
|
property HasUnknownFunction: Boolean read FHasUnknownFunction;
|
|
property OnProgress: TcxProgressEvent read FOnProgress write FOnProgress;
|
|
end;
|
|
|
|
{ TcxExcelFileWriter }
|
|
TcxExcelFileWriter = class(TComponent)
|
|
private
|
|
FCurrentFont: Word;
|
|
FCurrentFormat: TxlsDataFormat;
|
|
FCurrentStyle: Word;
|
|
FCurrentStylePtr: PxlsTreeNode;
|
|
FFontsList: TList;
|
|
FPageCount: Integer;
|
|
FPalette: TcxExcelPalette;
|
|
FPixelsPerInch: Integer;
|
|
FStorage: TxlsFileStorage;
|
|
FStylesList: TList;
|
|
FSST: TSSTStringTable;
|
|
procedure CreateDefaultFonts;
|
|
procedure CreateDefaultStyles;
|
|
procedure SetBlank(ACol, ARow: Word);
|
|
procedure SetBoolErr(ACol, ARow: Word; ABoolValue: Boolean);
|
|
procedure SetDouble(ACol, ARow: Word; ADoubleValue: Double);
|
|
procedure SetSSTString(ACol, ARow: Word; const AStringValue: WideString);
|
|
procedure SetWString(ACol, ARow: Word; const AStringValue: WideString);
|
|
protected
|
|
DefUnLockStyle: Integer;
|
|
function CalculateNodeSize(ANode: PxlsTreeNode; IsRoot: Boolean = False): Integer;
|
|
procedure ClearSheets(var APage: PxlsTreeNode); virtual;
|
|
procedure ClearStorage; virtual;
|
|
procedure ClearTree(var ANode: PxlsTreeNode); virtual;
|
|
function CompareXlsNodes(ANode1, ANode2: PxlsTreeNode): Boolean; virtual;
|
|
procedure CreateFont(AFont: PcxSSFontRec);
|
|
procedure CreateStorage; virtual;
|
|
procedure CreateStyle(AStyle: PcxSSCellStyleRec);
|
|
function CreateXlsListNode(const AType, ASize: Word;
|
|
const APrevNode: PxlsTreeNode = nil): PxlsTreeNode;
|
|
function FindNode(ANode: PxlsTreeNode; AType: Word): PxlsTreeNode; virtual;
|
|
procedure FreeData; virtual;
|
|
function GetEnd(ANode: PxlsTreeNode): PxlsTreeNode;
|
|
function GetRoot(ANode: PxlsTreeNode): PxlsTreeNode;
|
|
procedure SelectPage(APage: Word);
|
|
procedure SetPageDimension(APage: PxlsTreeNode; const AMaxCol, AMaxRow: Integer);
|
|
procedure StoreTreeNode(AStream: TStream; ANode: PxlsTreeNode;
|
|
const IsRoot: Boolean = False);
|
|
function UnionNodes(AFirstNode, ALastNode: PxlsTreeNode): PxlsTreeNode; virtual;
|
|
property CurrentStyle: Word read FCurrentStyle;
|
|
property CurrentFont: Word read FCurrentFont;
|
|
property CurrentFormat: TxlsDataFormat read FCurrentFormat;
|
|
property FontsList: TList read FFontsList;
|
|
property StylesList: TList read FStylesList;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure AddSheet(const ACaption: WideString;
|
|
VisibleGrid: Boolean = True; Visible: Boolean = True); virtual;
|
|
procedure Clear;
|
|
procedure DefineName(APName: PcxSSNameDefinition); virtual;
|
|
procedure SaveToStream(AStream: TStream); virtual;
|
|
procedure SetCellFunction(APage: Word; ACol, ARow: Word; const Value: TcxStackItem;
|
|
AExprSize: Word; const AParsedExpr: PByteArray);
|
|
procedure SetCellValue(APage: Word; ACol, ARow: Word;
|
|
const AValue: Variant; IsText: Boolean = False);
|
|
procedure SetColStyle(APage, ACol, AWidth: Word; IsLocked: Boolean; IsHidden: Boolean = False); virtual;
|
|
procedure SetDefaultColWidth(APage, ASize: Word);
|
|
procedure SetDefaultRowHeight(APage, ASize: Word);
|
|
procedure SetDefaultStyle(AStyle: PcxSSCellStyleRec);
|
|
procedure SetMergedCells(APage: Word; const ARects: array of TRect);
|
|
procedure SetProtection(APage: Integer; IsProtect: Boolean);
|
|
procedure SetPalette(APalette: PcxExcelPalette);
|
|
procedure SetRowStyle(APage, ARow, AHeight: Word; IsHidden: Boolean = False); virtual;
|
|
procedure SelectStyle(AStyle: PcxSSCellStyleRec); virtual;
|
|
property PageCount: Integer read FPageCount write FPageCount;
|
|
property PixelsPerInch: Integer read FPixelsPerInch write FPixelsPerInch;
|
|
end;
|
|
|
|
{ TSSTStringTable }
|
|
TSSTStringTable = class
|
|
private
|
|
FExtSST: TExtSST;
|
|
FSST: TSSTList;
|
|
FStringsInfo: TSSSTStringsInfo;
|
|
function GetStringCount: Integer;
|
|
function GetUniqueStringCount: Integer;
|
|
protected
|
|
procedure CreateExtSST(ASSTOffset: Integer); virtual;
|
|
function GetPackedSize: Integer; virtual;
|
|
function IndexOf(const AString: WideString): Integer; virtual;
|
|
procedure InsertStr(const AString: WideString); virtual;
|
|
property ExtSST: TExtSST read FExtSST;
|
|
property SST: TSSTList read FSST;
|
|
property StringsInfo: TSSSTStringsInfo read FStringsInfo;
|
|
public
|
|
constructor Create; virtual;
|
|
destructor Destroy; override;
|
|
function Add(const AString: WideString): Integer;
|
|
procedure Clear;
|
|
procedure SaveToStream(AStream: TStream; APosition: Integer = -1); virtual;
|
|
property PackedSize: Integer read GetPackedSize;
|
|
property TotalStringCount: Integer read GetStringCount;
|
|
property UniqueStringCount: Integer read GetUniqueStringCount;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
cxExcelFormulas;
|
|
|
|
type
|
|
{ TcxOLEFileHeader }
|
|
PcxOLEFileHeader = ^TcxOLEFileHeader;
|
|
TcxOLEFileHeader = packed record
|
|
Signature : Int64;
|
|
CLSID : array[0..1] of Int64;
|
|
OLEVersion : Integer;
|
|
ByteOrder : Word;
|
|
SectorShift : Word;
|
|
MiniSectorShift : Word;
|
|
Reserved : Word;
|
|
Reserved1 : LongInt;
|
|
Reserved2 : LongInt;
|
|
CountSectFAT : LongInt;
|
|
SectDIRStart : LongInt;
|
|
TransSignature : LongInt;
|
|
MiniSectorCutOff: LongInt;
|
|
SectMiniFATStart: LongInt;
|
|
CountSectMiniFAT: LongInt;
|
|
SectDIFStart : Longint;
|
|
CountSectDIF : LongInt;
|
|
SectFAT : array[0..108] of Integer;
|
|
end;
|
|
|
|
{ TcxOLEDIREntryType }
|
|
TcxOLEDIREntryType = (ET_INVALID, ET_STORAGE, ET_STREAM, ET_LOCKBYTES, ET_PROPERTY, ET_ROOT);
|
|
|
|
{ TcxOLEDIREntry }
|
|
PcxOLEDIREntry = ^TcxOLEDIREntry;
|
|
TcxOLEDIREntry = packed record
|
|
Name : array[0..64 - 1] of Byte;
|
|
NameLen : Word;
|
|
EntryType : TcxOLEDIREntryType;
|
|
BFlag : Byte;
|
|
LeftSib : LongWord;
|
|
RightSib : LongWord;
|
|
ChildSib : LongWord;
|
|
Guid : TGUID;
|
|
UserFlag : LongInt;
|
|
C_M_Time : array[0..1] of TTimeStamp;
|
|
StartSector: LongInt;
|
|
Size : LongInt;
|
|
Reserved : LongInt
|
|
end;
|
|
|
|
{ TcxFATSector }
|
|
PcxFATSector = ^TcxFATSector;
|
|
TcxFATSector = packed array[0..512 div SizeOf(Integer) - 1] of Integer;
|
|
|
|
PcxFATSectors = ^TcxFATSectors;
|
|
TcxFATSectors = packed array[0..MaxInt div SizeOf(TcxFATSector) - 1] of TcxFATSector;
|
|
|
|
{ TcxWorkBookReader }
|
|
TcxWorkBookWriter = class
|
|
protected
|
|
FBuffer: PByteArray;
|
|
FBufferSize: Integer;
|
|
FCapacity: Integer;
|
|
FFATBlockCount: Integer;
|
|
FIsSmallFile: Boolean;
|
|
FSectCount: Integer;
|
|
FStreamSize: Integer;
|
|
procedure CreateLocalFAT;
|
|
function GetDIFSector(ASector: Integer): PcxFATSector;
|
|
function GetDIREntry(AIndex: Integer): PcxOLEDIREntry;
|
|
function GetHeader: PcxOleFileHeader;
|
|
function GetFATSector(ASector: Integer): PcxFATSector;
|
|
protected
|
|
procedure Check(ACondition: Boolean);
|
|
procedure CreateDIF; virtual;
|
|
procedure CreateDIR; virtual;
|
|
procedure CreateFAT; virtual;
|
|
procedure CreateHeader; virtual;
|
|
procedure CreateSmallFAT; virtual;
|
|
procedure ReallocBuffer(const ASize: Integer);
|
|
property IsSmallFile: Boolean read FIsSmallFile;
|
|
property DIR[AEntry: Integer]: PcxOLEDIREntry read GetDIREntry;
|
|
property DIF[ASector: Integer]: PcxFATSector read GetDIFSector;
|
|
property FAT[ASector: Integer]: PcxFATSector read GetFATSector;
|
|
property Header: PcxOleFileHeader read GetHeader;
|
|
property Memory: PByteArray read FBuffer;
|
|
property Size: Integer read FBufferSize;
|
|
public
|
|
procedure CreateOLEStream(ADataSize: Integer; ADstStream: TStream);
|
|
end;
|
|
|
|
{ TcxWorkBookReader }
|
|
TcxWorkBookReader = class
|
|
private
|
|
FBuffer: Pointer;
|
|
FCurrentDIF: Integer;
|
|
FCurrentFAT: Integer;
|
|
FDIRSector: array[0..3] of TcxOLEDIREntry;
|
|
FDIF: TcxFATSector;
|
|
FFAT: TcxFATSector;
|
|
FHeader: TcxOLEFileHeader;
|
|
FLinSect: array of Integer;
|
|
FIsError: Boolean;
|
|
FIsSmallFile: Boolean;
|
|
FSectCount: Integer;
|
|
FStream: TStream;
|
|
FWorkBookDIR: Integer;
|
|
function GetBufferSize: Integer;
|
|
function GetDIFSector(ASector: Integer): PcxFATSector;
|
|
function GetDIREntry(AEntry: Integer): PcxOLEDIREntry;
|
|
function GetFATSector(ASector: Integer): PcxFATSector;
|
|
procedure GetSector(ASector: Integer; var AData: TcxFATSector);
|
|
function GetSmallFATSector(ASector: Integer): TcxFATSector;
|
|
protected
|
|
procedure CreateStreamSectorChain; virtual;
|
|
function IsSpecialSector(ASector: Integer): Boolean;
|
|
procedure ReadBuffer(var ABuf; const ASize: Integer; const APos: Integer);
|
|
procedure ReadStreamData; virtual;
|
|
procedure ReadWorkBookStream; virtual;
|
|
property DIF[ASector: Integer]: PcxFATSector read GetDIFSector;
|
|
property DIR[AEntry: Integer]: PcxOLEDIREntry read GetDIREntry;
|
|
property FAT[ASector: Integer]: PcxFATSector read GetFATSector;
|
|
property Header: TcxOleFileHeader read FHeader;
|
|
property SmallFAT[ASector: Integer]: TcxFATSector read GetSmallFATSector;
|
|
property IsSmallFile: Boolean read FIsSmallFile;
|
|
public
|
|
constructor Create(AStream: TStream);
|
|
destructor Destroy; override;
|
|
property Memory: Pointer read FBuffer;
|
|
property Size: Integer read GetBufferSize;
|
|
end;
|
|
|
|
// OLE data control codes
|
|
const
|
|
oleSignature = Int64($E11AB1A1E011CFD0); // header signature
|
|
oleDIFBlock = Integer($FFFFFFFC); // double inDIRect FAT
|
|
oleSpecBlock = Integer($FFFFFFFD); // special block begining
|
|
oleEndOfChain = Integer($FFFFFFFE); // end of chain
|
|
oleUnused = Integer($FFFFFFFF); // unused
|
|
oleEmpty = Integer($00000000); // empty
|
|
oleDLLVersion = Integer($0003003E); // specification version
|
|
olePlatformOrder = Word($FFFE); // order for intel platform
|
|
oleSectorsInMasterFAT = 109; // sectors in master FAT from header
|
|
oleBlockIDPerBigBlock = 128; // id cound in big block
|
|
oleMaxBlockIdInBigBlock = 127; // id cound in big block
|
|
oleContinueFATItem = 126; // id continue DIF block item
|
|
oleBigBlockShift = 9; // big block shift
|
|
oleSmallBlockShift = 6; // small block shift
|
|
oleReservedSectorCount = 2; // header and DIRecotry sectors
|
|
oleMiniSectorMaxSize = Integer($000001000);// max minisector size
|
|
oleSmallBlockSize = 1 shl oleSmallBlockShift; // size of small block
|
|
oleBigBlockSize = 1 shl oleBigBlockShift; // size of big block in bytes
|
|
oleDIRBlockSize = SizeOf(TcxOLEDIREntry); // size of DIRectory block in bytes
|
|
oleIndexSize = SizeOf(Integer); // size of index
|
|
|
|
oleRoot : WideString = 'Root Entry';
|
|
oleWorkbook : WideString = 'Workbook';
|
|
oleInvalidName: WideString = '';
|
|
|
|
SectorSize: array[Boolean] of Integer = (oleBigBlockSize, oleSmallBlockSize);
|
|
|
|
// Default OLE Storage header template
|
|
oleHeaderTemplate: TcxOLEFileHeader =
|
|
(Signature : oleSignature;
|
|
CLSID : (oleEmpty, oleEmpty);
|
|
OLEVersion : oleDLLVersion;
|
|
ByteOrder : olePlatformOrder;
|
|
SectorShift : oleBigBlockShift;
|
|
MiniSectorShift : oleSmallBlockShift;
|
|
Reserved : oleEmpty;
|
|
Reserved1 : oleEmpty;
|
|
Reserved2 : oleEmpty;
|
|
CountSectFAT : 1;
|
|
SectDIRStart : 1;
|
|
TransSignature : oleEmpty;
|
|
MiniSectorCutOff: oleMiniSectorMaxSize;
|
|
SectMiniFATStart: oleEndOfChain;
|
|
CountSectMiniFAT: oleEmpty;
|
|
SectDIFStart : oleEndOfChain;
|
|
CountSectDIF : oleEmpty);
|
|
|
|
FillStyles: array[0..17] of Byte = ($01, $03, $02, $04, $11, $12,
|
|
$05, $06, $07, $08, $09, $0A, $0B, $0C, $0D, $0E, $0F, $10);
|
|
|
|
MaxBlockSize = 8192;
|
|
ScaledFactor: Double = 1;
|
|
ScreenResolution: Integer = 96;
|
|
|
|
procedure AddStringToBlock(ASource: Pointer; var ADest: TSSTBlock; ASize: Word);
|
|
begin
|
|
with ADest do
|
|
begin
|
|
Data[DataSize] := 1;
|
|
Inc(DataSize);
|
|
Move(ASource^, Data[DataSize], ASize);
|
|
Inc(DataSize, ASize);
|
|
end;
|
|
end;
|
|
|
|
function ConvertRkNumber(AValue: Integer): string;
|
|
var
|
|
AV: Double;
|
|
begin
|
|
AV := 0;
|
|
PIntArray(@AV)^[1] := Integer(AValue and $FFFFFFFC);
|
|
case AValue and 3 of
|
|
1:
|
|
AV := AV / 100;
|
|
2:
|
|
AV := Integer(AValue and $FFFFFFFC) / 4;
|
|
3:
|
|
AV := Integer(AValue and $FFFFFFFC) / 400;
|
|
end;
|
|
Result := FloatToStr(AV);
|
|
end;
|
|
|
|
function FillStyleToXlsFillStyle(AStyle: TcxSSFillStyle; ABkColor, AFgColor: Word): Integer;
|
|
begin
|
|
Result := 0;
|
|
Inc(ABkColor, 8);
|
|
Inc(AFgColor, 8);
|
|
if AFgColor = $41 then
|
|
AFgColor := $40;
|
|
if not ((AStyle = fsSolid) and
|
|
((AFgColor = $40) or ((ABkColor in [$40, $41]) and (AFgColor = 9)))) then
|
|
begin
|
|
PWordArray(@Result)^[0] := FillStyles[Byte(AStyle)] shl 10;
|
|
PWordArray(@Result)^[1] := (ABkColor and $7F shl 7) or (AFgColor and $7F);
|
|
end;
|
|
end;
|
|
|
|
function GetHashCode(const Buffer; Count: Integer): Word; assembler;
|
|
asm
|
|
MOV ECX,EDX
|
|
MOV EDX,EAX
|
|
XOR EAX,EAX
|
|
@@1:
|
|
ROL AX,5
|
|
XOR AL,[EDX]
|
|
INC EDX
|
|
DEC ECX
|
|
JNE @@1
|
|
end;
|
|
|
|
procedure IncPtr(var ASrc: PByteArray; ALen: Integer = 1);
|
|
begin
|
|
ASrc := Pointer(Integer(ASrc) + ALen);
|
|
end;
|
|
|
|
function ReadExcelString2(var ASource: PByteArray; ALen: Integer): string; overload;
|
|
var
|
|
AKey: Integer;
|
|
AFormatCount: Word;
|
|
AWStr: WideString;
|
|
AStr: AnsiString;
|
|
StartPos: Integer;
|
|
IsUnicode: Boolean;
|
|
begin
|
|
StartPos := 0;
|
|
Result := '';
|
|
AKey := ASource^[StartPos];
|
|
AFormatCount := 0;
|
|
if (AKey and $08) <> 0 then
|
|
begin
|
|
AFormatCount := PWordArray(@ASource^[StartPos + 1])^[0];
|
|
Inc(StartPos, 2)
|
|
end;
|
|
IsUnicode := AKey and $01 <> 0;
|
|
Inc(StartPos, 1);
|
|
if ALen > 0 then
|
|
begin
|
|
if IsUnicode then
|
|
begin
|
|
SetLength(AWStr, ALen);
|
|
Move(ASource^[StartPos], AWStr[1], ALen * SizeOf(WideChar));
|
|
Result := AWStr;
|
|
Inc(StartPos, ALen * 2);
|
|
end
|
|
else
|
|
begin
|
|
SetLength(AStr, ALen);
|
|
Move(ASource^[StartPos], AStr[1], ALen);
|
|
Result := dxAnsiStringToString(AStr);
|
|
Inc(StartPos, ALen);
|
|
end;
|
|
end;
|
|
Inc(StartPos, AFormatCount * 4);
|
|
IncPtr(ASource, StartPos);
|
|
end;
|
|
|
|
function ReadExcelString(const AData: Pointer): string;
|
|
var
|
|
AChars: PByteArray;
|
|
begin
|
|
AChars := @PWordArray(AData)^[1];
|
|
Result := ReadExcelString2(AChars, PWordArray(AData)^[0]);
|
|
end;
|
|
|
|
function ReadExcelString1(const AData: Pointer; ALen: Byte): string;
|
|
var
|
|
AChars: PByteArray;
|
|
begin
|
|
AChars := AData;
|
|
Result := ReadExcelString2(AChars, ALen);
|
|
end;
|
|
|
|
function RoundDiv(const Number, Denominator: Integer): Integer;
|
|
begin
|
|
Result := Number div Denominator;
|
|
if (Number mod Denominator) <> 0 then
|
|
Inc(Result);
|
|
end;
|
|
|
|
function XlsColorToColor(AIndex: Word; ADefault: Word): Word;
|
|
begin
|
|
if AIndex = 0 then
|
|
Result := ADefault
|
|
else
|
|
Result := AIndex - 8;
|
|
end;
|
|
|
|
{ TcxExcelFileReader }
|
|
constructor TcxExcelFileReader.Create(AOwner: TObject);
|
|
begin
|
|
FReader := nil;
|
|
FSharedStringTable := TStringList.Create;
|
|
if not {$IFNDEF DELPHI5}cxSSTypes.{$ENDIF}Supports(AOwner, IcxSpreadSheetBook, FOwner) then
|
|
FOwner := nil;
|
|
FFuncConverter := TcxFormulaReader.Create(Self);
|
|
FPixelsPerInch := ScreenResolution;
|
|
FBoundSheets := TStringList.Create;
|
|
end;
|
|
|
|
destructor TcxExcelFileReader.Destroy;
|
|
begin
|
|
FBoundSheets.Free;
|
|
FSharedStringTable.Free;
|
|
FFuncConverter.Free;
|
|
FStyleList.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TcxExcelFileReader.AssignStream(AStream: TStream): Boolean;
|
|
begin
|
|
FReader := TcxWorkBookReader.Create(AStream);
|
|
Result := (MemoryData <> nil) and (DataSize > 0)
|
|
end;
|
|
|
|
procedure TcxExcelFileReader.OpenStream;
|
|
begin
|
|
FHasUnknownFunction := False;
|
|
if FReader <> nil then
|
|
try
|
|
DoReadRecords;
|
|
finally
|
|
FreeAndNil(FReader);
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileReader.DoReadRecords;
|
|
var
|
|
ABiffRecHeader: PBiffRecHeader;
|
|
APosition: Integer;
|
|
|
|
function ConstructFormula(ACol, ARow: Integer; Tokens: PByteArray; ParsedLen: Word): string;
|
|
begin
|
|
Result := TcxFormulaReader(FFuncConverter).ConvertFormulas(ACol, ARow, Tokens, ParsedLen);
|
|
FHasUnknownFunction := FHasUnknownFunction or TcxFormulaReader(FFuncConverter).UnknownFunction;
|
|
end;
|
|
|
|
procedure ReadFontInfo(AData: Pointer);
|
|
var
|
|
ACount: Word;
|
|
begin
|
|
ACount := Length(FFontArray);
|
|
SetLength(FFontArray, ACount + 1);
|
|
with FFontArray[ACount] do
|
|
begin
|
|
if (PWordArray(AData)^[1] and $2) <> 0 then
|
|
Include(Style, fsItalic);
|
|
if (PWordArray(AData)^[1] and $8) <> 0 then
|
|
Include(Style, fsStrikeOut);
|
|
if PWordArray(AData)^[3] <> $190 then
|
|
Include(Style, fsBold);
|
|
if PByteArray(AData)^[10] <> 0 then
|
|
Include(Style, fsUnderline);
|
|
Size := Round(PWordArray(AData)^[0] / 20);
|
|
Charset := TFontCharset(PByteArray(AData)^[12]);
|
|
if Integer(Charset) = 0 then
|
|
Charset := DEFAULT_CHARSET;
|
|
Name := ReadExcelString1(@PByteArray(AData)^[15], PByteArray(AData)^[14]);
|
|
FontColor := XlsColorToColor(PWordArray(AData)^[2], cxSSDefaultColorValue);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
ASheetIndex: Integer;
|
|
|
|
procedure ReadBoundSheet(AData: Pointer);
|
|
var
|
|
AFlag: Byte;
|
|
Position: Integer;
|
|
begin
|
|
AFlag := (PWordArray(AData)^[2] shr 8) and 3;
|
|
if AFlag in [0, 1] then
|
|
Position := PInteger(AData)^
|
|
else
|
|
Position := 0;
|
|
FBoundSheets.AddObject(ReadExcelString1(@PByteArray(AData)^[7],
|
|
PByteArray(AData)^[6]), Pointer(Position));
|
|
if AFlag in [0, 1] then
|
|
begin
|
|
Owner.AddSheet(FBoundSheets[FBoundSheets.Count - 1], AFlag = 1);
|
|
if PWordArray(AData)^[2] in [1, 2] then
|
|
Owner.SetPageVisible(ASheetIndex, False);
|
|
Inc(ASheetIndex);
|
|
end;
|
|
end;
|
|
|
|
// procedure ReadBoundSheet(AData: Pointer);
|
|
// var
|
|
// Position: Integer;
|
|
// begin
|
|
// if PWordArray(AData)^[2] = 0 then
|
|
// Position := PInteger(AData)^
|
|
// else
|
|
// Position := 0;
|
|
// FBoundSheets.AddObject(ReadExcelString1(@PByteArray(AData)^[7],
|
|
// PByteArray(AData)^[6]), Pointer(Position));
|
|
// if PWordArray(AData)^[2] = 0 then
|
|
// Owner.AddSheet(FBoundSheets[FBoundSheets.Count - 1], (PByteArray(AData)^[5] and $01) <> 0);
|
|
// end;
|
|
|
|
procedure ReadColumnFormatInfo(AData: Pointer);
|
|
var
|
|
I: Integer;
|
|
ALocked: Boolean;
|
|
AWords: PWordArray;
|
|
begin
|
|
AWords := AData;
|
|
for I := AWords^[0] to AWords^[1] do
|
|
begin
|
|
ALocked := cLocked in FXFRecords[AWords^[3]].CellState;
|
|
FCurrentSheet.SetColRowSize(htCol, I,
|
|
Round(AWords^[2] / 36.6 * ScaledFactor), ALocked, not Boolean(AWords^[4] and $01));
|
|
end;
|
|
end;
|
|
|
|
procedure ReadRowFormatInfo(AData: Pointer);
|
|
begin
|
|
if (PWordArray(AData)^[6] and $80) = $80 then Exit;
|
|
FCurrentSheet.SetColRowSize(htRow, PWordArray(AData)^[0],
|
|
Round(PWordArray(AData)^[3] / 20 * 1.325 * ScaledFactor), False,
|
|
(PWordArray(AData)^[6] and $20) = 0)
|
|
end;
|
|
|
|
procedure ReadStdColWidth(AData: Pointer);
|
|
begin
|
|
FCurrentSheet.SetDefaultSize(htCol, Round(PWord(AData)^ / 262 * 8.25 * ScaledFactor));
|
|
end;
|
|
|
|
procedure ReadDefaultColWidth(AData: Pointer);
|
|
begin
|
|
FCurrentSheet.SetDefaultSize(htCol, Round(PWord(AData)^ * 8.38 * ScaledFactor));
|
|
end;
|
|
|
|
procedure ReadDefaultRowHeight(AData: Pointer);
|
|
begin
|
|
FCurrentSheet.SetDefaultSize(htRow, Round(PWordArray(AData)^[1] / 12.7));
|
|
end;
|
|
|
|
procedure ReadSST(ASSTData: Pointer; ASize: Integer);
|
|
var
|
|
BufSize: Word;
|
|
StartPtr: Pointer;
|
|
ACurrentPtr: Pointer;
|
|
|
|
function ReadStringChars(ALen: Integer; IsWideChar: Boolean): AnsiString;
|
|
var
|
|
AWChars: WideString;
|
|
AEndsCount: Integer;
|
|
ACount: Integer;
|
|
begin
|
|
ACount := ALen;
|
|
AEndsCount := (Integer(StartPtr) + BufSize) - Integer(ACurrentPtr);
|
|
try
|
|
if IsWideChar then
|
|
begin
|
|
while (ACount * 2) > AEndsCount do Dec(ACount);
|
|
SetLength(AWChars, ACount);
|
|
Move(ACurrentPtr^, AWChars[1], ACount * 2);
|
|
Result := dxStringToAnsiString(AWChars);
|
|
Inc(Integer(ACurrentPtr), ACount);
|
|
end
|
|
else
|
|
begin
|
|
if ACount > AEndsCount then
|
|
ACount := AEndsCount;
|
|
SetLength(Result, ACount);
|
|
Move(ACurrentPtr^, Result[1], ACount);
|
|
end;
|
|
finally
|
|
Inc(Integer(ACurrentPtr), ACount)
|
|
end;
|
|
if (ALen - ACount) > 0 then
|
|
begin
|
|
if PWord(ACurrentPtr)^ <> brcContinue then
|
|
raise EdxException.Create(cxGetResourceString(@scxSpreadSheetErrorReadSST));
|
|
StartPtr := @PWordArray(ACurrentPtr)^[2];
|
|
BufSize := PWordArray(ACurrentPtr)^[1];
|
|
ACurrentPtr := StartPtr;
|
|
IsWideChar := PByte(ACurrentPtr)^ <> 0;
|
|
Inc(Integer(ACurrentPtr));
|
|
Result := Result + ReadStringChars(ALen - ACount, IsWideChar);
|
|
end;
|
|
end;
|
|
|
|
function ReadNextString: AnsiString;
|
|
var
|
|
ALen: Word;
|
|
AOptions: Byte;
|
|
FormatTokens: Word;
|
|
|
|
procedure ReadMultiByteChars;
|
|
var
|
|
ExtRstLen: Integer;
|
|
begin
|
|
if (AOptions and $08) <> 0 then
|
|
begin
|
|
FormatTokens := PWord(ACurrentPtr)^;
|
|
Inc(Integer(ACurrentPtr), 2);
|
|
end;
|
|
ExtRstLen := PInteger(ACurrentPtr)^;
|
|
Inc(Integer(ACurrentPtr), SizeOf(Integer));
|
|
Result := ReadStringChars(ALen, (AOptions and $01) <> 0);
|
|
Inc(Integer(ACurrentPtr), ExtRstLen);
|
|
end;
|
|
|
|
begin
|
|
Result := '';
|
|
FormatTokens := 0;
|
|
if ((Integer(StartPtr) + BufSize) - Integer(ACurrentPtr)) < 3 then
|
|
begin
|
|
if PWord(ACurrentPtr)^ <> brcContinue then
|
|
raise EdxException.Create(cxGetResourceString(@scxSpreadSheetErrorReadSST));
|
|
StartPtr := @PWordArray(ACurrentPtr)^[2];
|
|
BufSize := PWordArray(ACurrentPtr)^[1];
|
|
ACurrentPtr := StartPtr;
|
|
end;
|
|
ALen := PWord(ACurrentPtr)^;
|
|
Inc(Integer(ACurrentPtr), 2);
|
|
AOptions := PByte(ACurrentPtr)^;
|
|
Inc(Integer(ACurrentPtr));
|
|
if ALen <> 0 then
|
|
try
|
|
if (AOptions and $04) <> 0 then
|
|
ReadMultiByteChars
|
|
else
|
|
begin
|
|
if (AOptions and $08) <> 0 then
|
|
begin
|
|
FormatTokens := PWord(ACurrentPtr)^;
|
|
Inc(Integer(ACurrentPtr), 2);
|
|
end;
|
|
Result := ReadStringChars(ALen, (AOptions and $01) <> 0);
|
|
end;
|
|
finally
|
|
FormatTokens := FormatTokens * 4;
|
|
if FormatTokens > (Integer(StartPtr) + BufSize - Integer(ACurrentPtr)) then
|
|
begin
|
|
FormatTokens := FormatTokens - (Integer(StartPtr) + BufSize - Integer(ACurrentPtr));
|
|
if PWord(@PByteArray(StartPtr)^[BufSize])^ <> brcContinue then
|
|
raise EdxException.Create(cxGetResourceString(@scxSpreadSheetErrorReadSST));
|
|
ACurrentPtr := @PByteArray(StartPtr)^[BufSize + 4];
|
|
BufSize := PWord(@PByteArray(StartPtr)^[BufSize + 2])^;
|
|
StartPtr := ACurrentPtr;
|
|
end;
|
|
Inc(Integer(ACurrentPtr), FormatTokens);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
I: Integer;
|
|
AbsPtr: Integer;
|
|
AStringCount: Integer;
|
|
begin
|
|
AStringCount := PIntArray(ASSTData)^[1];
|
|
FSharedStringTable.Clear;
|
|
FSharedStringTable.Capacity := AStringCount;
|
|
StartPtr := @PIntArray(ASSTData)^[2];
|
|
BufSize := ASize - 8;
|
|
ACurrentPtr := StartPtr;
|
|
AbsPtr := Integer(StartPtr);
|
|
for I := 0 to AStringCount - 1 do
|
|
begin
|
|
ProgressUpdate(StartPtr);
|
|
FSharedStringTable.Add(dxAnsiStringToString(ReadNextString));
|
|
if (Integer(ACurrentPtr) - Integer(AbsPtr)) > FExtRecordSize then
|
|
raise EdxException.Create(cxGetResourceString(@scxExcelImportUndefinedString));
|
|
end;
|
|
end;
|
|
|
|
procedure ReadStyleInfo(AData: Pointer);
|
|
var
|
|
S: AnsiString;
|
|
N: Word;
|
|
I: Integer;
|
|
begin
|
|
N := PWord(AData)^;
|
|
SetLength(S, PByteArray(AData)^[2]);
|
|
Move(PByteArray(AData)^[3 + 2], S[1], Length(S));
|
|
if FStyleList = nil then
|
|
begin
|
|
FStyleList := TStringList.Create;
|
|
for I := 0 to High(ExcelDefaultFormats) do
|
|
FStyleList.AddObject(ExcelDefaultFormats[I], TObject(I));
|
|
end;
|
|
if N > $31 then
|
|
FStyleList.AddObject(dxAnsiStringToString(S), TObject(N));
|
|
end;
|
|
|
|
procedure ReadXFRecord(AData: Pointer);
|
|
var
|
|
ACount: Integer;
|
|
AXFData: PWordArray;
|
|
ASide: TcxSSEdgeBorder;
|
|
I: Integer;
|
|
begin
|
|
AXFData := AData;
|
|
ACount := Length(FXFRecords);
|
|
SetLength(FXFRecords, ACount + 1);
|
|
with FXFRecords[ACount] do
|
|
begin
|
|
if AXFData[0] >= 4 then Dec(AXFData[0]);
|
|
if AXFData[0] >= Length(FFontArray) then
|
|
FontPtr := @FFontArray[Length(FFontArray) - 1]
|
|
else
|
|
FontPtr := @FFontArray[AXFData[0]];
|
|
if (AXFData^[4] and $00010) <> 0 then
|
|
Include(CellState, cMerge);
|
|
if (AXFData^[4] and $0010) <> 0 then
|
|
Include(CellState, cShrinkToFit);
|
|
if (AXFData^[2] and $1) <> 0 then
|
|
Include(CellState, cLocked);
|
|
if (AXFData^[3] and $7) >= 6 then
|
|
HorzAlign := haCenter
|
|
else
|
|
HorzAlign := TcxHorzTextAlign(AXFData^[3] and $7);
|
|
VertAlign := TcxVertTextAlign(AXFData^[3] shr 4 and $7);
|
|
WordBreak := (AXFData^[3] and $0008) <> 0;
|
|
Borders[eLeft].Style := TcxSSEdgeLineStyle(AXFData^[5] and $000F);
|
|
Borders[eRight].Style := TcxSSEdgeLineStyle((AXFData^[5] and $00F0) shr 4);
|
|
Borders[eTop].Style := TcxSSEdgeLineStyle((AXFData^[5] and $0F00) shr 8);
|
|
Borders[eBottom].Style := TcxSSEdgeLineStyle((AXFData^[5] and $F000) shr 12);
|
|
for ASide := eLeft to eBottom do
|
|
if Byte(Borders[ASide].Style) >= Byte(lsNone) then
|
|
Borders[ASide].Style := lsDefault;
|
|
Borders[eLeft].Color :=
|
|
XlsColorToColor(AXFData^[6] and $007F, cxSSDefaultColorValue);
|
|
Borders[eRight].Color :=
|
|
XlsColorToColor((AXFData^[6] and $3F80) shr 7, cxSSDefaultColorValue);
|
|
Borders[eTop].Color :=
|
|
XlsColorToColor(AXFData^[7] and $007F, cxSSDefaultColorValue);
|
|
Borders[eBottom].Color :=
|
|
XlsColorToColor((AXFData^[7] and $3F80) shr 7, cxSSDefaultColorValue);
|
|
BrushStyle := ConvertBrushStyles(AXFData^[8] shr 10 and $003F);
|
|
if (AXFData^[8] shr 10 and $003F) <> 0 then
|
|
begin
|
|
if BrushStyle <> fsSolid then
|
|
begin
|
|
BrushBkColor := AXFData^[9] shr 7 and $7F - 8;
|
|
BrushFgColor := AXFData^[9] and $7F - 8;
|
|
end
|
|
else
|
|
begin
|
|
BrushBkColor := AXFData^[9] and $7F - 8;
|
|
BrushFgColor := AXFData^[9] shr 7 and $7F - 8;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
BrushBkColor := cxSSDefaultColorValue;
|
|
BrushFgColor := cxSSDefaultColorValue;
|
|
end;
|
|
FormatIndex := AXFData^[1];
|
|
if (FormatIndex > $31) then
|
|
begin
|
|
I := FStyleList.Count - 1;
|
|
while I >= 0 do
|
|
begin
|
|
if Integer(FStyleList.Objects[I]) = FormatIndex then
|
|
begin
|
|
CheckFormat(FStyleList[I], FormatIndex);
|
|
Break;
|
|
end
|
|
else
|
|
Dec(I);
|
|
end;
|
|
if I < 0 then
|
|
FormatIndex := 0;
|
|
end;
|
|
end;
|
|
if ACount = 15 then
|
|
Owner.SetDefaultStyle(@FXFRecords[ACount])
|
|
end;
|
|
|
|
procedure ReadColors(AData: Pointer);
|
|
begin
|
|
if PWord(AData)^ >= 55 then
|
|
Owner.SetPalette(@PWordArray(AData)^[1]);
|
|
end;
|
|
|
|
procedure ReadMergedCells(AData: Pointer);
|
|
var
|
|
I, ACount: Integer;
|
|
ARect: TRect;
|
|
ACoords: PWordArray;
|
|
begin
|
|
ACoords := AData;
|
|
ACount := ACoords^[0];
|
|
Inc(Integer(ACoords), 2);
|
|
for I := 0 to ACount - 1 do
|
|
begin
|
|
with ARect do
|
|
begin
|
|
Top := ACoords^[0];
|
|
Bottom := ACoords^[1];
|
|
Left := ACoords^[2];
|
|
Right := ACoords^[3];
|
|
end;
|
|
Inc(Integer(ACoords), 4 * 2);
|
|
CurrentSheet.SetMergedCells(ARect, True);
|
|
end;
|
|
end;
|
|
|
|
procedure AddCell(ACol, ARow: Integer; AText: string;
|
|
AStyleIndex: Integer; IsFormula: Boolean = False; IsBlank: Boolean = False);
|
|
begin
|
|
with FCurrentSheet.GetCell(ACol, ARow) do
|
|
begin
|
|
if (AStyleIndex <> -1) and (AStyleIndex < Length(FXFRecords)) then
|
|
GetCellStyle.SetCellStyle(@FXFRecords[AStyleIndex]);
|
|
if IsFormula and (Pos(scxRefError, AText) <> 0) then
|
|
begin
|
|
IsFormula := False;
|
|
AText := scxRefError;
|
|
end;
|
|
if (AText <> '') or IsBlank then
|
|
SetTextEx(AText, IsFormula, False);
|
|
end;
|
|
end;
|
|
|
|
procedure ReadBoolErr(AData: Pointer);
|
|
var
|
|
S: string;
|
|
begin
|
|
if PByteArray(AData)^[7] = 0 then
|
|
S := BoolToStr(Boolean(PByteArray(AData)^[6]))
|
|
else
|
|
S := XLSErrToStr(PByteArray(AData)^[6]);
|
|
AddCell(PWordArray(AData)^[1], PWordArray(AData)^[0], S, PWordArray(AData)^[2]);
|
|
end;
|
|
|
|
procedure ReadLabelSST(AData: Pointer);
|
|
begin
|
|
AddCell(PWordArray(AData)^[1], PWordArray(AData)^[0],
|
|
FSharedStringTable[PInteger(@PByteArray(AData)^[6])^], PWordArray(AData)^[2]);
|
|
end;
|
|
|
|
procedure ReadBlank(AData: Pointer);
|
|
begin
|
|
AddCell(PWordArray(AData)^[1], PWordArray(AData)^[0], '', PWordArray(AData)^[2], False, True);
|
|
end;
|
|
|
|
procedure ReadMulBlank(AData: Pointer; ASize: Integer);
|
|
var
|
|
I: Integer;
|
|
ARow: Integer;
|
|
AColFirst, AColLast: Integer;
|
|
begin
|
|
ARow := PWordArray(AData)^[0];
|
|
AColFirst := PWordArray(AData)^[1];
|
|
AColLast := AColFirst + (ASize div 2 - 4);
|
|
Inc(Integer(AData), 4);
|
|
for I := AColFirst to AColLast do
|
|
begin
|
|
AddCell(I, ARow, '', PWord(AData)^);
|
|
Inc(Integer(AData), 2);
|
|
end;
|
|
end;
|
|
|
|
procedure ReadNumber(AData: Pointer);
|
|
begin
|
|
AddCell(PWordArray(AData)^[1], PWordArray(AData)^[0],
|
|
FloatToStr(PDouble(@PWordArray(AData)^[3])^), PWordArray(AData)^[2])
|
|
end;
|
|
|
|
procedure ReadRK(AData: Pointer);
|
|
begin
|
|
AddCell(PWordArray(AData)^[1], PWordArray(AData)^[0],
|
|
ConvertRkNumber(PInteger(@PWordArray(AData)^[3])^), PWordArray(AData)^[2]);
|
|
end;
|
|
|
|
procedure ReadMulRk(AData: Pointer; ASize: Integer);
|
|
var
|
|
I, ARow: Integer;
|
|
AFirstCol, ALastCol: Integer;
|
|
begin
|
|
ARow := PWordArray(AData)^[0];
|
|
AFirstCol := PWordArray(AData)^[1];
|
|
ALastCol := AFirstCol + (ASize - 6) div 6 - 1;
|
|
Inc(Integer(AData), 4);
|
|
for I := AFirstCol to ALastCol do
|
|
begin
|
|
AddCell(I, ARow, ConvertRkNumber(PInteger(@PByteArray(AData)^[2])^), PWord(AData)^);
|
|
Inc(Integer(AData), 6);
|
|
end;
|
|
end;
|
|
|
|
procedure ReadSimpleLabel(AData: Pointer);
|
|
begin
|
|
AddCell(PWordArray(AData)^[1], PWordArray(AData)^[0],
|
|
ReadExcelString(@PWordArray(AData)^[3]), PWordArray(AData)^[2]);
|
|
end;
|
|
|
|
procedure ReadName(AData: Pointer);
|
|
|
|
function CheckPage(APage: Integer): Integer;
|
|
begin
|
|
if APage >= FBoundSheets.Count then
|
|
Result := 0
|
|
else
|
|
Result := APage;
|
|
end;
|
|
|
|
function ReadNameDefinition(ATokens: PByteArray; ALen: Word;
|
|
var ADef: TcxSSNameDefinition): Boolean;
|
|
var
|
|
PWords: PWordArray;
|
|
begin
|
|
Result := True;
|
|
case ATokens^[0] of
|
|
ptgArea, ptgAreaV:
|
|
with ADef.Definition do
|
|
begin
|
|
Page := FCurrentPage;
|
|
PWords := @ATokens^[1];
|
|
Area := TRange(Rect(PWords^[2], PWords^[0], PWords^[3], PWords^[1]));
|
|
end;
|
|
ptgArea3d, ptgArea3dV:
|
|
with ADef.Definition do
|
|
begin
|
|
Page := XltPage(CheckPage(PWord(@ATokens^[1])^));
|
|
PWords := @ATokens^[1];
|
|
Area := TRange(Rect(PWords^[3] and $3FFF, PWords^[1],
|
|
PWords^[4] and $3FFF, PWords^[2]));
|
|
end;
|
|
ptgRef3d, ptgRef3dV:
|
|
with ADef.Definition do
|
|
begin
|
|
PWords := @ATokens^[1];
|
|
Page := XltPage(CheckPage(PWords^[0]));
|
|
Area := TRange(Rect(PWords^[2], PWords^[1], PWords^[2], PWords^[1]));
|
|
end;
|
|
ptgRef, ptgRefV:
|
|
with ADef.Definition do
|
|
begin
|
|
Page := FCurrentPage;
|
|
PWords := @ATokens^[1];
|
|
Area := TRange(Rect(PWords^[1], PWords^[0], PWords^[1], PWords^[0]));
|
|
end;
|
|
else
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
ACount: Integer;
|
|
ANameDef: TcxSSnameDefinition;
|
|
begin
|
|
ACount := Length(FNames);
|
|
SetLength(FNames, ACount + 1);
|
|
FNames[ACount] := ReadExcelString1(@PByteArray(AData)^[14], PByteArray(AData)^[3]);
|
|
if (PWordArray(AData)^[2] > 0) and (PWord(AData)^ = 0) and
|
|
ReadNameDefinition(@PByteArray(AData)^[PByteArray(AData)^[3] + 15], PWordArray(AData)^[2], ANameDef) then
|
|
Owner.DefineName(FNames[ACount], ANameDef.Definition.Page, TRect(ANameDef.Definition.Area));
|
|
end;
|
|
|
|
procedure ReadFormula(AData: Pointer);
|
|
begin
|
|
AddCell(PWordArray(AData)^[1], PWordArray(AData)^[0],
|
|
ConstructFormula(PWordArray(AData)^[1], PWordArray(AData)^[0],
|
|
@PWordArray(AData)^[11], PWordArray(AData)^[10]), PWordArray(AData)^[2], True)
|
|
end;
|
|
|
|
procedure ReadSharedFormula(AData: Pointer);
|
|
var
|
|
I, J: Integer;
|
|
begin
|
|
for J := PWordArray(AData)^[0] to PWordArray(AData)^[1] do
|
|
for I := PByteArray(AData)^[4] to PByteArray(AData)^[5] do
|
|
begin
|
|
AddCell(I, J, ConstructFormula(I, J, @PWordArray(AData)^[5],
|
|
PWordArray(AData)^[4]), -1, True);
|
|
end;
|
|
end;
|
|
|
|
procedure ReadFormulaArray(AData: Pointer);
|
|
var
|
|
I, J: Integer;
|
|
begin
|
|
for J := PWordArray(AData)^[0] to PWordArray(AData)^[1] do
|
|
for I := PByteArray(AData)^[4] to PByteArray(AData)^[5] do
|
|
begin
|
|
with CurrentSheet.GetCell(I, J) do
|
|
SetText(ConstructFormula(I, J, @PWordArray(AData)^[7], PWordArray(AData)^[6]));
|
|
end;
|
|
end;
|
|
|
|
procedure ReadGridSet(AData: Pointer);
|
|
begin
|
|
end;
|
|
|
|
procedure ReadViewHeaders(AData: Pointer);
|
|
begin
|
|
end;
|
|
|
|
procedure ReadDimension(AData: Pointer);
|
|
begin
|
|
CurrentSheet.SetPageDimension(Min(PWordArray(AData)^[5], 255),
|
|
Min(PIntArray(AData)^[1], 65535));
|
|
end;
|
|
|
|
procedure ReadExternSheet(AData: pointer);
|
|
var
|
|
PWords: PWordArray;
|
|
I: Integer;
|
|
begin
|
|
PWords := @PWordArray(AData)^[2];
|
|
SetLength(FSheetXlt, (FExtRecordSize - 2) div 6);
|
|
for I := 0 to Length(FSheetXlt) - 1 do
|
|
FSheetXlt[I] := PWords^[I * 3];
|
|
end;
|
|
|
|
procedure ReadForFile(AData: Pointer);
|
|
var
|
|
I: Integer;
|
|
S: string;
|
|
begin
|
|
for I := 0 to ABiffRecHeader^.RecSize div 2 - 1 do
|
|
begin
|
|
if I <> 0 then
|
|
S := S + ', ';
|
|
S := S + IntToHex(PWordArray(AData)^[I], 4);
|
|
end;
|
|
end;
|
|
|
|
function ReadBofRecord: PBiffRecHeader;
|
|
var
|
|
ANextRec: PBiffRecHeader;
|
|
begin
|
|
FExtRecordSize := 0;
|
|
if (APosition + 4) <= DataSize then
|
|
begin
|
|
Result := Pointer(Integer(MemoryData) + APosition);
|
|
FExtRecordSize := Result^.RecSize;
|
|
ANextRec := Pointer(Integer(Result) + Result^.RecSize + 4);
|
|
while ANextRec^.RecType = brcContinue do
|
|
begin
|
|
FExtRecordSize := FExtRecordSize + ANextRec^.RecSize + 4;
|
|
ANextRec := Pointer(Integer(ANextRec) + ANextRec^.RecSize + 4);
|
|
end;
|
|
end
|
|
else
|
|
Result := nil;
|
|
Inc(APosition, FExtRecordSize + 4);
|
|
end;
|
|
|
|
procedure ReadWindow2(AData: Pointer);
|
|
var
|
|
AFlag: Word;
|
|
begin
|
|
AFlag := PWord(AData)^;
|
|
FCurrentSheet.SetViewInformation(Rect(0, 0, 0, 0), (AFlag and $1) <> 0,
|
|
(AFlag and $2) <> 0, True, True);
|
|
end;
|
|
|
|
function IsValidPage(APosition: Integer): Boolean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := False;
|
|
for I := 0 to FBoundSheets.Count - 1 do
|
|
begin
|
|
Result := Integer(FBoundSheets.Objects[I]) = APosition;
|
|
if Result then Break
|
|
end;
|
|
end;
|
|
|
|
procedure ReadProtection(AData: Pointer);
|
|
begin
|
|
if FCurrentSheet <> nil then
|
|
FCurrentSheet.SetProtection(Boolean(PWord(AData)^ and $1))
|
|
else
|
|
Owner.SetProtection(Boolean(PWord(AData)^ and $1));
|
|
end;
|
|
|
|
var
|
|
APage: Integer;
|
|
begin
|
|
if Owner = nil then
|
|
Exit;
|
|
ASheetIndex := 0;
|
|
FCurrentPage := -1;
|
|
APosition := 0;
|
|
APage := -2;
|
|
FProgress := -1;
|
|
ABiffRecHeader := MemoryData;
|
|
try
|
|
while ((APosition + 4) < DataSize)and(FCurrentPage < Owner.GetPageCount) do
|
|
begin
|
|
ProgressUpdate(ABiffRecHeader);
|
|
ABiffRecHeader := ReadBofRecord;
|
|
case ABiffRecHeader^.RecType of
|
|
brcBof:
|
|
begin
|
|
Inc(APage);
|
|
if APage >= 0 then
|
|
begin
|
|
while not IsValidPage(APosition - SizeOf(BOF) - 4) do
|
|
begin
|
|
ABiffRecHeader := ReadBofRecord;
|
|
if ABiffRecHeader = nil then Break;
|
|
end;
|
|
if ABiffRecHeader = nil then Break;
|
|
{while PWord(@ABiffRecHeader^.RecData[2])^ <> $10 do
|
|
begin
|
|
APosition := Integer(FBoundSheets.Objects[APage]);
|
|
ABiffRecHeader := Pointer(Integer(MemoryData) + Integer(FBoundSheets.Objects[APage]));
|
|
ProgressUpdate(ABiffRecHeader);
|
|
Inc(APage);
|
|
end;}
|
|
Inc(FCurrentPage);
|
|
end;
|
|
if (FCurrentPage >= 0) and (FCurrentPage < Owner.GetPageCount) then
|
|
FCurrentSheet := Owner.GetSheet(FCurrentPage);
|
|
end;
|
|
brcBoundSheet:
|
|
ReadBoundSheet(@ABiffRecHeader^.RecData);
|
|
brcPrintgridlines:
|
|
ReadGridSet(@ABiffRecHeader^.RecData);
|
|
brcPrintHeaders:
|
|
ReadViewHeaders(@ABiffRecHeader^.RecData);
|
|
brcProtect:
|
|
ReadProtection(@ABiffRecHeader^.RecData);
|
|
brcFont:
|
|
ReadFontInfo(@ABiffRecHeader^.RecData);
|
|
brcColInfo:
|
|
ReadColumnFormatInfo(@ABiffRecHeader^.RecData);
|
|
brcROW:
|
|
ReadRowFormatInfo(@ABiffRecHeader^.RecData);
|
|
brcDefColWidth:
|
|
ReadDefaultColWidth(@ABiffRecHeader^.RecData);
|
|
brcSTANDARDWIDTH:
|
|
ReadStdColWidth(@ABiffRecHeader^.RecData);
|
|
brcDefaultRowHeight:
|
|
ReadDefaultRowHeight(@ABiffRecHeader^.RecData);
|
|
brcSST:
|
|
ReadSST(@ABiffRecHeader^.RecData, ABiffRecHeader.RecSize);
|
|
brcDIMENSIONS:
|
|
ReadDimension(@ABiffRecHeader^.RecData);
|
|
brcBlank :
|
|
ReadBlank(@ABiffRecHeader^.RecData);
|
|
brcBoolErr:
|
|
ReadBoolErr(@ABiffRecHeader^.RecData);
|
|
brcMergeCells:
|
|
ReadMergedCells(@ABiffRecHeader^.RecData);
|
|
brcLabelSST:
|
|
ReadLabelSST(@ABiffRecHeader^.RecData);
|
|
brcLabel:
|
|
ReadSimpleLabel(@ABiffRecHeader^.RecData);
|
|
brcNumber:
|
|
ReadNumber(@ABiffRecHeader^.RecData);
|
|
brcMULBLANK:
|
|
ReadMulBlank(@ABiffRecHeader^.RecData, FExtRecordSize);
|
|
brcMulRk:
|
|
ReadMulRk(@ABiffRecHeader^.RecData, FExtRecordSize);
|
|
brcRK:
|
|
ReadRk(@ABiffRecHeader^.RecData);
|
|
brcRString:
|
|
ReadSimpleLabel(@ABiffRecHeader^.RecData);
|
|
brcArray:
|
|
ReadFormulaArray(@ABiffRecHeader^.RecData);
|
|
brcSHRFMLA:
|
|
ReadSharedFormula(@ABiffRecHeader^.RecData);
|
|
brcFORMULA:
|
|
ReadFormula(@ABiffRecHeader^.RecData);
|
|
brcName:
|
|
ReadName(@ABiffRecHeader^.RecData);
|
|
brcExternSheet:
|
|
ReadExternSheet(@ABiffRecHeader^.RecData);
|
|
brcFORMAT:
|
|
ReadStyleInfo(@ABiffRecHeader^.RecData);
|
|
brcXF:
|
|
ReadXFRecord(@ABiffRecHeader^.RecData);
|
|
brcPALETTE:
|
|
ReadColors(@ABiffRecHeader^.RecData);
|
|
brcIndex:
|
|
ReadForFile(@ABiffRecHeader^.RecData);
|
|
brcWindow2:
|
|
ReadWindow2(@ABiffRecHeader^.RecData);
|
|
brcFILEPASS:
|
|
begin
|
|
if ABiffRecHeader^.RecType = brcFILEPASS then
|
|
raise EdxException.Create(cxGetResourceString(@scxSpreadSheetInvalidStreamFormat));
|
|
end;
|
|
else
|
|
DoReadUnknownRecord(ABiffRecHeader^);
|
|
end;
|
|
ProgressUpdate(ABiffRecHeader);
|
|
end;
|
|
finally
|
|
ProgressUpdate(Pointer(Integer(MemoryData) + DataSize));
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileReader.DoReadUnknownRecord(var ARec: TBiffRecHeader);
|
|
begin
|
|
end;
|
|
|
|
procedure TcxExcelFileReader.ProgressUpdate(APos: Pointer);
|
|
var
|
|
ALeft: Byte;
|
|
begin
|
|
ALeft :=
|
|
Round((Integer(APos) - Integer(MemoryData)) * 100 / DataSize);
|
|
if ALeft <> FProgress then
|
|
begin
|
|
FProgress := ALeft;
|
|
if Assigned(FOnProgress) then
|
|
FOnProgress(Self, FProgress);
|
|
end;
|
|
end;
|
|
|
|
function TcxExcelFileReader.XltPage(const APage: Word): Word;
|
|
begin
|
|
if APage < Length(FSheetXlt) then
|
|
Result := FSheetXlt[APage]
|
|
else
|
|
Result := APage;
|
|
if Result >= BoundSheets.Count then
|
|
Result := APage;
|
|
end;
|
|
|
|
function TcxExcelFileReader.ConvertBrushStyles(ABrushStyle: Byte): TcxSSFillStyle;
|
|
var
|
|
I: Byte;
|
|
begin
|
|
Result := fsSolid;
|
|
for I := 0 to High(FillStyles) do
|
|
if FillStyles[I] = ABrushStyle then
|
|
begin
|
|
Result := TcxSSFillStyle(I);
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
function TcxExcelFileReader.GetDataSize: Integer;
|
|
begin
|
|
if FReader <> nil then
|
|
Result := TcxWorkBookReader(FReader).Size
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
function TcxExcelFileReader.GetMemoryData: Pointer;
|
|
begin
|
|
if (FReader <> nil) and (TcxWorkBookReader(FReader).Memory <> nil) then
|
|
Result := TcxWorkBookReader(FReader).Memory
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
{ TcxExcelFileWriter }
|
|
constructor TcxExcelFileWriter.Create(AOwner: TComponent);
|
|
begin
|
|
FPixelsPerInch := ScreenResolution;
|
|
FSST := TSSTStringTable.Create;
|
|
CreateStorage;
|
|
end;
|
|
|
|
destructor TcxExcelFileWriter.Destroy;
|
|
begin
|
|
FreeData;
|
|
FSST.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.AddSheet(const ACaption: WideString;
|
|
VisibleGrid: Boolean = True; Visible: Boolean = True);
|
|
|
|
function AddBoundSheet: PxlsTreeNode;
|
|
begin
|
|
Result := GetRoot(FStorage.RootHeader);
|
|
while Result^.Next^.RecType <> brcSUPBOOK do
|
|
Result := Result^.Next;
|
|
Result := CreateXlsListNode(brcBoundSheet, 8 + Length(ACaption) * 2, Result);
|
|
Result^.ByteData[9] := Byte(not Visible);
|
|
Result^.ByteData[10] := Length(ACaption);
|
|
Result^.ByteData[11] := IsUnicodeStr;
|
|
Move(ACaption[1], Result^.ByteData[12], Result^.ByteData[10] * 2);
|
|
end;
|
|
|
|
var
|
|
ANode: PxlsTreeNode;
|
|
APageId: Integer;
|
|
|
|
begin
|
|
FStorage.Pages := GetEnd(FStorage.Pages);
|
|
FStorage.Pages := CreateXlsListNode(0, SizeOf(TxlsSheetInfo), FStorage.Pages);
|
|
FillChar(FStorage.Pages^.SheetInfo, SizeOf(TxlsSheetInfo), 0);
|
|
with FStorage.Pages^ do
|
|
begin
|
|
Move(BOF, SheetInfo.BOF[4], SizeOf(BOF));
|
|
PWord(@SheetInfo.BOF)^ := $0809;
|
|
PWordArray(@SheetInfo.BOF)^[1] := SizeOf(BOF);
|
|
SheetInfo.BOF[6] := $10;
|
|
SheetNodesList := CreateXlsListNode(brcIndex, 16, SheetNodesList);
|
|
SheetInfo.LinkToIndex := SheetNodesList;
|
|
SheetNodesList := CreateXlsListNode(brcDIMENSIONS, 14, SheetNodesList);
|
|
SheetInfo.LinkToDimension := SheetNodesList;
|
|
SheetNodesList := CreateXlsListNode(brcWINDOW2, 18, SheetNodesList);
|
|
SheetInfo.LinkToWindow2 := SheetNodesList;
|
|
if Prev <> nil then
|
|
begin
|
|
SheetInfo.SheetId := Prev^.SheetInfo.SheetId + 1;
|
|
SheetInfo.LinkToWindow2^.IntData[1] := $B6;
|
|
end
|
|
else
|
|
begin
|
|
SheetInfo.SheetId := 0;
|
|
SheetInfo.LinkToWindow2^.IntData[1] := $6B6;
|
|
end;
|
|
if not VisibleGrid then
|
|
SheetInfo.LinkToWindow2^.WordData[2] :=
|
|
SheetInfo.LinkToWindow2^.WordData[2] and not $2;
|
|
APageId := SheetInfo.SheetId;
|
|
SheetInfo.LinkToBoundSheet := AddBoundSheet;
|
|
end;
|
|
ANode := GetRoot(FStorage.RootHeader);
|
|
while ANode^.RecType <> brcTABID do ANode := ANode^.Next;
|
|
ANode^.RecSize := (APageId + 1) * 2;
|
|
ANode^.WordData[2 + APageId] := APageId + 1;
|
|
ANode := GetEnd(ANode);
|
|
while (ANode <> nil) and (ANode^.RecType <> brcSUPBOOK) do ANode := ANode^.Prev;
|
|
if ANode <> nil then ANode^.WordData[2] := APageId + 1;
|
|
while ANode^.RecType <> brcExternSheet do ANode := ANode^.Next;
|
|
if ANode <> nil then
|
|
with ANode^ do
|
|
begin
|
|
Inc(WordData[2]);
|
|
WordData[4 + APageId * 3] := APageId;
|
|
WordData[5 + APageId * 3] := APageId;
|
|
ANode.RecSize := (APageId + 1) * 6 + 2;
|
|
end;
|
|
ANode := CreateXlsListNode(brcRefMode, 2, GetEnd(FStorage.Pages).SheetNodesList);
|
|
ANode.WordData[2] := 1;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.Clear;
|
|
begin
|
|
try
|
|
ClearStorage;
|
|
finally
|
|
CreateStorage;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.DefineName(APName: PcxSSNameDefinition);
|
|
var
|
|
ANode: PxlsTreeNode;
|
|
ANameDef: array of Byte;
|
|
|
|
procedure CreateNameDef;
|
|
begin
|
|
with APName^.Definition do
|
|
begin
|
|
SetLength(ANameDef, 11);
|
|
ANameDef[0] := ptgArea3D;
|
|
if Page >= PageCount then
|
|
PWord(@ANameDef[1])^ := 0
|
|
else
|
|
PWord(@ANameDef[1])^ := Page;
|
|
PWord(@ANameDef[3])^ := Area.Top and $FFFF;
|
|
PWord(@ANameDef[5])^ := Area.Bottom and $FFFF;
|
|
PWord(@ANameDef[7])^ := Area.Left and $FF;
|
|
PWord(@ANameDef[9])^ := Area.Right and $FF;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
SetLength(ANameDef, 0);
|
|
CreateNameDef;
|
|
ANode := GetEnd(FStorage.RootHeader);
|
|
ANode := CreateXlsListNode(brcName, Length(APName^.Name) + Length(ANameDef) + 15, ANode);
|
|
with ANode^ do
|
|
begin
|
|
WordData[2] := Byte(APName^.IsDeleted);
|
|
ByteData[7] := Length(APName^.Name);
|
|
PWord(@ByteData[8])^ := Length(ANameDef);
|
|
ByteData[18] := 0; // todo: code flag
|
|
Move(dxStringToAnsiString(APName^.Name)[1], ByteData[19], Length(APName^.Name));
|
|
if Length(ANameDef) > 0 then
|
|
Move(ANameDef[0], ByteData[18 + Length(APName^.Name) + 1], Length(ANameDef));
|
|
end;
|
|
SetLength(ANameDef, 0);
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SaveToStream(AStream: TStream);
|
|
|
|
function SetSheetSizeToBoundSheet: Integer;
|
|
var
|
|
ANode: PxlsTreeNode;
|
|
begin
|
|
Result := CalculateNodeSize(GetRoot(FStorage.RootHeader), True) +
|
|
+ FSST.PackedSize;
|
|
ANode := GetRoot(FStorage.Pages);
|
|
while ANode <> nil do
|
|
begin
|
|
PInteger(@ANode^.SheetInfo.LinkToBoundSheet^.WordData[2])^ := Result;
|
|
Result := Result + CalculateNodeSize(ANode);
|
|
ANode := ANode^.Next;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
I: Integer;
|
|
ANode: PxlsTreeNode;
|
|
ASize: Integer;
|
|
const
|
|
AEmpty: Byte = 0;
|
|
begin
|
|
ASize := SetSheetSizeToBoundSheet;
|
|
with TcxWorkBookWriter.Create do
|
|
try
|
|
CreateOLEStream(ASize, AStream);
|
|
StoreTreeNode(AStream, FStorage.RootHeader, True);
|
|
ANode := GetRoot(FStorage.Pages);
|
|
while ANode <> nil do
|
|
begin
|
|
StoreTreeNode(AStream, ANode);
|
|
ANode := ANode^.Next;
|
|
end;
|
|
if (ASize mod oleBigBlockSize) > 0 then
|
|
for I := 0 to oleBigBlockSize - (ASize mod oleBigBlockSize) - 1 do
|
|
AStream.WriteBuffer(AEmpty, SizeOf(Byte));
|
|
finally
|
|
Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SetCellFunction(APage: Word; ACol, ARow: Word;
|
|
const Value: TcxStackItem; AExprSize: Word; const AParsedExpr: PByteArray);
|
|
var
|
|
ANode: PxlsTreeNode;
|
|
begin
|
|
SelectPage(APage);
|
|
SetPageDimension(FStorage.Pages, ACol, ARow);
|
|
ANode := CreateXlsListNode(brcFORMULA, AExprSize + 22, GetEnd(FStorage.Pages^.SheetNodesList));
|
|
begin
|
|
ANode^.WordData[2] := ARow;
|
|
ANode^.WordData[3] := ACol;
|
|
ANode^.WordData[4] := FCurrentStyle;
|
|
ANode^.WordData[9] := 3;
|
|
ANode^.WordData[12] := AExprSize;
|
|
Move(AParsedExpr^, ANode^.WordData[13], AExprSize);
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SetCellValue(APage: Word; ACol, ARow: Word;
|
|
const AValue: Variant; IsText: Boolean = False);
|
|
var
|
|
AFloatValue: Double;
|
|
AWString: WideString;
|
|
begin
|
|
SelectPage(APage);
|
|
FStorage.Pages^.SheetNodesList := GetEnd(FStorage.Pages^.SheetNodesList);
|
|
SetPageDimension(FStorage.Pages, ACol, ARow);
|
|
case VarType(AValue) of
|
|
varEmpty:
|
|
SetBlank(ACol, ARow);
|
|
varSmallint, varInteger, varByte, varDate, varSingle, varDouble, varCurrency:
|
|
SetDouble(ACol, ARow, AValue);
|
|
varOleStr, varString{$IFDEF DELPHI12}, varUString {$ENDIF}:
|
|
begin
|
|
AWString := AValue;
|
|
if AWString = '' then
|
|
SetBlank(ACol, ARow)
|
|
else
|
|
begin
|
|
if not IsText and cxTryStrToFloat(AWString, AFloatValue) then
|
|
SetDouble(ACol, ARow, AFloatValue)
|
|
else
|
|
begin
|
|
if Length(AWString) > 250 then
|
|
SetSSTString(ACol, ARow, AWString)
|
|
else
|
|
SetWString(ACol, ARow, AWString);
|
|
end;
|
|
end;
|
|
end;
|
|
varBoolean:
|
|
SetBoolErr(ACol, ARow, AValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SetColStyle(APage, ACol, AWidth: Word; IsLocked: Boolean;
|
|
IsHidden: Boolean = False);
|
|
var
|
|
ANode: PxlsTreeNode;
|
|
begin
|
|
SelectPage(APage);
|
|
ANode := GetEnd(FStorage.Pages^.SheetNodesList);
|
|
ANode := CreateXlsListNode(brcColInfo, 11, ANode);
|
|
with ANode^ do
|
|
begin
|
|
WordData[2] := ACol;
|
|
WordData[3] := ACol;
|
|
WordData[4] := Round(AWidth * 36.6 / ScaledFactor);
|
|
if IsLocked then
|
|
WordData[5] := DefUnLockStyle
|
|
else
|
|
WordData[5] := 1;
|
|
WordData[6] := Byte(IsHidden);
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SetDefaultColWidth(APage, ASize: Word);
|
|
var
|
|
ANode: PxlsTreeNode;
|
|
begin
|
|
SelectPage(APage);
|
|
ANode := GetEnd(FStorage.Pages.SheetNodesList);
|
|
ANode := CreateXlsListNode(brcDefColWidth, 4, ANode);
|
|
with ANode^ do
|
|
WordData[2] := Round(ASize / 8.38);
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SetDefaultRowHeight(APage, ASize: Word);
|
|
begin
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SetDefaultStyle(AStyle: PcxSSCellStyleRec);
|
|
var
|
|
DefStyle: TcxSSCellStyleRec;
|
|
begin
|
|
DefStyle := AStyle^;
|
|
with FCurrentStylePtr^.Prev^ do
|
|
begin
|
|
WordData[4] := (WordData[4] and not $1) or Byte(cLocked in AStyle^.CellState);
|
|
WordData[5] := Byte(AStyle^.HorzAlign) or (Byte(AStyle^.WordBreak) shl 3)
|
|
or (Byte(AStyle^.VertAlign) shl 4);
|
|
CreateFont(AStyle^.FontPtr);
|
|
WordData[2] := FCurrentFont;
|
|
end;
|
|
CreateStyle(@DefStyle);
|
|
DefUnLockStyle := FCurrentStyle;
|
|
with FCurrentStylePtr^ do
|
|
WordData[4] := (WordData[4] and not $1) or Byte(not (cLocked in AStyle^.CellState));
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SetMergedCells(APage: Word; const ARects: array of TRect);
|
|
var
|
|
ANode: PxlsTreeNode;
|
|
I, ACount, Offset: Integer;
|
|
|
|
function AddMergedNode(ACount: Integer; AParent: PxlsTreeNode;
|
|
IsParent: Boolean = False): PxlsTreeNode;
|
|
begin
|
|
Result := CreateXlsListNode(brcMergeCells, ACount shl 3 + 2, AParent);
|
|
Result^.WordData[2] := ACount;
|
|
Offset := 0;
|
|
end;
|
|
|
|
procedure WriteRect(AData: PWordArray; const ARect: TRect);
|
|
begin
|
|
with ARect do
|
|
begin
|
|
AData^[0] := Top and $FFFF;
|
|
AData^[1] := Bottom and $FFFF;
|
|
AData^[2] := Left and $FF;
|
|
AData^[3] := Right and $FF;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
ACount := Length(ARects);
|
|
if ACount > 0 then
|
|
begin
|
|
SelectPage(APage);
|
|
ANode := AddMergedNode(Min(ACount, 1024), GetEnd(FStorage.Pages^.SheetNodesList), True);
|
|
for I := 1 to ACount do
|
|
begin
|
|
WriteRect(@ANode^.WordData[3 + Offset * 4], ARects[I - 1]);
|
|
Inc(Offset);
|
|
if (I mod 1024) = 0 then
|
|
ANode := AddMergedNode(Min(ACount - I, 1024), ANode);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SetPalette(APalette: PcxExcelPalette);
|
|
var
|
|
I: Integer;
|
|
APalNode: PxlsTreeNode;
|
|
const
|
|
APredefinedStyles: array[0..5, 0..3] of Byte =
|
|
(($10, $80, $03, $FF),
|
|
($11, $80, $06, $FF),
|
|
($10, $80, $04, $FF), // currency
|
|
($10, $80, $07, $FF), // currency
|
|
($00, $80, $00, $FF),
|
|
($10, $80, $05, $FF));// perncent
|
|
begin
|
|
FPalette := APalette^;
|
|
APalNode := FindNode(GetRoot(FStorage.RootHeader), brcPalette);
|
|
if APalNode^.RecType <> brcPalette then
|
|
begin
|
|
while APalNode^.RecType <> brcXF do APalNode := APalNode^.Prev;
|
|
APalNode := CreateXlsListNode(brcPalette, SizeOf(TcxExcelPalette) + 2, APalNode);
|
|
APalNode^.WordData[2] := SizeOf(TcxExcelPalette) div 4;
|
|
end;
|
|
Move(APalette^, APalNode^.WordData[3], SizeOf(TcxExcelPalette));
|
|
// TODO: Style dialog in MsExcel generate AV bug
|
|
if (APalNode.Next = nil) or (APalNode.Next.RecType <> brcSTYLE) then
|
|
begin
|
|
for I := Low(APredefinedStyles) to High(APredefinedStyles) do
|
|
begin
|
|
APalNode := CreateXlsListNode(brcStyle, 4, APalNode);
|
|
Move(APredefinedStyles[I], APalNode^.ByteData[4], 4);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SetProtection(APage: Integer; IsProtect: Boolean);
|
|
var
|
|
ANode: PxlsTreeNode;
|
|
begin
|
|
if APage < 0 then
|
|
ANode := GetEnd(FStorage.RootHeader)
|
|
else
|
|
begin
|
|
SelectPage(APage);
|
|
ANode := GetEnd(FStorage.Pages^.SheetNodesList);
|
|
end;
|
|
ANode := CreateXlsListNode(brcProtect, SizeOf(Word), ANode);
|
|
ANode^.WordData[2] := Byte(IsProtect);
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SetRowStyle(APage, ARow, AHeight: Word;
|
|
IsHidden: Boolean = False);
|
|
var
|
|
ANode: PxlsTreeNode;
|
|
begin
|
|
SelectPage(APage);
|
|
ANode := GetEnd(FStorage.Pages^.SheetNodesList);
|
|
ANode := CreateXlsListNode(brcRow, 16, ANode);
|
|
with ANode^ do
|
|
begin
|
|
WordData[2] := ARow;
|
|
WordData[3] := 0;
|
|
WordData[4] := 256;
|
|
WordData[5] := Round((AHeight * 20) / (1.325 * ScaledFactor));
|
|
WordData[8] := 320 and not $80;
|
|
if IsHidden then
|
|
WordData[8] := WordData[8] or $20;
|
|
WordData[9] := 16;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SelectStyle(AStyle: PcxSSCellStyleRec);
|
|
begin
|
|
CreateStyle(AStyle);
|
|
end;
|
|
|
|
function TcxExcelFileWriter.CalculateNodeSize(ANode: PxlsTreeNode;
|
|
IsRoot: Boolean = False): Integer;
|
|
begin
|
|
Result := 0;
|
|
if (ANode <> nil) and (not IsRoot) then
|
|
begin
|
|
Result := SizeOf(BOF) + 4;
|
|
ANode := GetRoot(ANode^.SheetNodesList);
|
|
end;
|
|
while ANode <> nil do
|
|
begin
|
|
Inc(Result, ANode^.WordData[1] + 4);
|
|
ANode := ANode^.Next;
|
|
end;
|
|
Inc(Result, 4);
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.ClearSheets(var APage: PxlsTreeNode);
|
|
|
|
procedure DoClear(var ANode: PxlsTreeNode);
|
|
begin
|
|
if ANode <> nil then
|
|
try
|
|
with ANode^ do
|
|
begin
|
|
if Next <> nil then
|
|
DoClear(Next);
|
|
ClearTree(SheetNodesList);
|
|
end;
|
|
finally
|
|
FreeMem(ANode);
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
APage := GetRoot(APage);
|
|
try
|
|
DoClear(APage);
|
|
finally
|
|
APage := nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.ClearStorage;
|
|
begin
|
|
try
|
|
ClearTree(FStorage.RootHeader);
|
|
ClearSheets(FStorage.Pages);
|
|
FSST.Clear;
|
|
finally
|
|
FillChar(FStorage, SizeOf(FStorage), 0);
|
|
FStylesList.Free;
|
|
FFontsList.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.ClearTree(var ANode: PxlsTreeNode);
|
|
begin
|
|
if ANode <> nil then
|
|
try
|
|
ANode := GetEnd(ANode);
|
|
while ANode^.Prev <> nil do
|
|
begin
|
|
ANode := ANode^.Prev;
|
|
FreeMem(ANode^.Next);
|
|
end;
|
|
finally
|
|
FreeMem(ANode);
|
|
ANode := nil;
|
|
end;
|
|
end;
|
|
|
|
function TcxExcelFileWriter.CompareXlsNodes(ANode1, ANode2: PxlsTreeNode): Boolean;
|
|
begin
|
|
Result := (ANode1^.RecType = ANode2^.RecType) and (ANode1^.RecSize = ANode2^.RecSize)
|
|
and CompareMem(@ANode1^.ByteData, @ANode2^.ByteData, ANode1^.RecSize)
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.CreateFont(AFont: PcxSSFontRec);
|
|
var
|
|
AFonts,
|
|
AFontNode: PxlsTreeNode;
|
|
AIndex: Integer;
|
|
AWName: WideString;
|
|
const
|
|
ABold: array[Boolean] of Word = ($190, $2BC);
|
|
AItalic: array[Boolean] of Word = (0, $02);
|
|
AStrikeOut: array[Boolean] of Word = (0, 9);
|
|
begin
|
|
AIndex := FFontsList.IndexOf(AFont);
|
|
if AIndex < 0 then
|
|
begin
|
|
with AFont^ do
|
|
begin
|
|
AFontNode := CreateXlsListNode(brcFont, Length(Name) * 2 + 16);
|
|
AFontNode^.WordData[2] := Size * 20;
|
|
AFontNode^.WordData[3] := AItalic[fsItalic in Style] + AStrikeOut[fsStrikeOut in Style];
|
|
AFontNode^.WordData[5] := ABold[fsBold in Style];
|
|
AFontNode^.ByteData[14] := Byte(fsUnderline in Style);
|
|
if Integer(Charset) = 1 then
|
|
AFontNode^.ByteData[16] := 0
|
|
else
|
|
AFontNode^.ByteData[16] := Byte(Charset);
|
|
AFontNode^.WordData[4] := FontColor + 8;
|
|
if AFontNode^.WordData[4] > 64 then
|
|
AFontNode^.WordData[4] := 32767;
|
|
AWName := Name;
|
|
AFontNode^.ByteData[18] := Length(AWName);
|
|
AFontNode^.ByteData[19] := 1;
|
|
Move(AWName[1], AFontNode^.ByteData[20], AFontNode^.ByteData[18] * 2);
|
|
AFonts := FindNode(GetRoot(FStorage.RootHeader), brcFont);
|
|
FCurrentFont := 2;
|
|
while (AFonts^.Next <> nil) and (AFonts^.Next^.RecType = brcFont) do
|
|
begin
|
|
AFonts := AFonts^.Next;
|
|
Inc(FCurrentFont);
|
|
end;
|
|
UnionNodes(AFonts, AFontNode);
|
|
FFontsList.Add(AFont);
|
|
end;
|
|
end
|
|
else
|
|
FCurrentFont := AIndex + 5;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.CreateStorage;
|
|
begin
|
|
FStylesList := TList.Create;
|
|
FFontsList := TList.Create;
|
|
FStorage.RootHeader := CreateXlsListNode(brcBOF, SizeOf(BOF));
|
|
Move(BOF, FStorage.RootHeader.ByteData[4], SizeOf(BOF));
|
|
FStorage.RootHeader.ByteData[6] := $05;
|
|
FStorage.RootHeader := CreateXlsListNode(brcTABID, 1024, FStorage.RootHeader);
|
|
FStorage.RootHeader := CreateXlsListNode(brcWINDOW1, SizeOf(WINDOW1), FStorage.RootHeader);
|
|
Move(WINDOW1, FStorage.RootHeader.ByteData[4], SizeOf(WINDOW1));
|
|
CreateDefaultFonts;
|
|
CreateDefaultStyles;
|
|
FCurrentFont := 0;
|
|
FCurrentFormat := $31;
|
|
FCurrentStyle := 0;
|
|
FStorage.RootHeader := CreateXlsListNode(brcSUPBOOK, 4, GetEnd(FStorage.RootHeader));
|
|
with FStorage.RootHeader^ do
|
|
begin
|
|
WordData[2] := $0015;
|
|
WordData[3] := $0401;
|
|
end;
|
|
FStorage.RootHeader := CreateXlsListNode(brcExternSheet, 1024, FStorage.RootHeader);
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.CreateStyle(AStyle: PcxSSCellStyleRec);
|
|
var
|
|
AIndex: Integer;
|
|
I: Byte;
|
|
AFillStyle: Integer;
|
|
ALineStyle: TcxSSEdgeLineStyle;
|
|
AColor: Word;
|
|
const
|
|
ALocked: array[Boolean] of Byte = (0, 1);
|
|
AHidden: array[Boolean] of Byte = (0, 2);
|
|
AColorShift: array[0..3] of Byte = (0, 7, 16, 23);
|
|
ALeftRightBorders: array[0..3] of TcxSSEdgeBorder = (eLeft, eRight, eTop, eBottom);
|
|
XFStyleState = $0400 or $0800 or $1000 or $2000 or $4000 or $8000;
|
|
begin
|
|
AIndex := FStylesList.IndexOf(AStyle);
|
|
if AIndex < 0 then
|
|
begin
|
|
FCurrentStylePtr := CreateXlsListNode(brcXF, SizeOf(DefaultStylesTable[0]), FCurrentStylePtr);
|
|
with FCurrentStylePtr^ do
|
|
begin
|
|
CreateFont(AStyle^.FontPtr);
|
|
Move(DefaultStylesTable[15], WordData[2], SizeOf(DefaultStylesTable[0]));
|
|
WordData[2] := FCurrentFont;
|
|
FCurrentFormat := AStyle^.FormatIndex;
|
|
WordData[3] := FCurrentFormat;
|
|
WordData[4] := ALocked[cLocked in AStyle^.CellState] or AHidden[False];
|
|
WordData[5] := Byte(AStyle^.HorzAlign) or (Byte(AStyle^.WordBreak) shl 3)
|
|
or (Byte(AStyle^.VertAlign) shl 4);
|
|
for I := 0 to 3 do
|
|
with AStyle^.Borders[ALeftRightBorders[I]] do
|
|
begin
|
|
ALineStyle := Style;
|
|
if Byte(ALineStyle) >= Byte(lsNone) then
|
|
ALineStyle := lsDefault;
|
|
WordData[7] := WordData[7] or Byte(ALineStyle) shl (4 * I);
|
|
AColor := Color + 8;
|
|
if AColor > 64 then
|
|
AColor := 64;
|
|
IntData[4] := IntData[4] or AColor shl AColorShift[I];
|
|
end;
|
|
with AStyle^ do
|
|
begin
|
|
if BrushStyle <> fsSolid then
|
|
AFillStyle := FillStyleToXlsFillStyle(BrushStyle, BrushBkColor, BrushFGColor)
|
|
else
|
|
AFillStyle := FillStyleToXlsFillStyle(BrushStyle, BrushFGColor, BrushBkColor);
|
|
end;
|
|
PWord(@ByteData[12])^ := PWord(@ByteData[12])^ or XFStyleState;
|
|
if AFillStyle <> 0 then
|
|
begin
|
|
PInteger(@WordData[10])^ := PInteger(@WordData[10])^ and not $FFFFFF00;
|
|
PInteger(@WordData[10])^ := PInteger(@WordData[10])^ or AFillStyle;
|
|
end;
|
|
if cMerge in AStyle^.CellState then
|
|
ByteData[12] := ByteData[12] or $20;
|
|
FCurrentStyle := Length(DefaultStylesTable) + FStylesList.Add(AStyle);
|
|
end
|
|
end
|
|
else
|
|
FCurrentStyle := Length(DefaultStylesTable) + AIndex;
|
|
end;
|
|
|
|
function TcxExcelFileWriter.CreateXlsListNode(const AType, ASize: Word;
|
|
const APrevNode: PxlsTreeNode): PxlsTreeNode;
|
|
begin
|
|
Result := AllocMem(ASize + SizeOf(Word) * 2 + SizeOf(Pointer) * 2);
|
|
Result^.Prev := APrevNode;
|
|
Result^.RecType := AType;
|
|
Result^.RecSize := ASize;
|
|
if APrevNode <> nil then
|
|
begin
|
|
Result^.Next := APrevNode^.Next;
|
|
if Result^.Next <> nil then
|
|
Result^.Next^.Prev := Result;
|
|
APrevNode^.Next := Result;
|
|
end;
|
|
end;
|
|
|
|
function TcxExcelFileWriter.FindNode(ANode: PxlsTreeNode; AType: Word): PxlsTreeNode;
|
|
begin
|
|
Result := ANode;
|
|
while (Result <> nil) and (Result^.Next <> nil) and (Result^.RecType <> AType) do
|
|
Result := Result^.Next;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.FreeData;
|
|
begin
|
|
ClearStorage;
|
|
end;
|
|
|
|
function TcxExcelFileWriter.GetEnd(ANode: PxlsTreeNode): PxlsTreeNode;
|
|
begin
|
|
Result := ANode;
|
|
while (Result <> nil) and (Result^.Next <> nil) do
|
|
Result := Result^.Next;
|
|
end;
|
|
|
|
function TcxExcelFileWriter.GetRoot(ANode: PxlsTreeNode): PxlsTreeNode;
|
|
begin
|
|
Result := ANode;
|
|
while (Result <> nil) and (Result^.Prev <> nil) do
|
|
Result := Result^.Prev;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SelectPage(APage: Word);
|
|
begin
|
|
with FStorage do
|
|
begin
|
|
while (Pages^.Next <> nil) and (Pages^.SheetInfo.SheetId < APage) do
|
|
Pages := Pages^.Next;
|
|
while (Pages^.Prev <> nil) and (Pages^.SheetInfo.SheetId > APage) do
|
|
Pages := Pages^.Prev;
|
|
Pages^.SheetNodesList := GetEnd(Pages^.SheetNodesList);
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SetPageDimension(APage: PxlsTreeNode;
|
|
const AMaxCol, AMaxRow: Integer);
|
|
begin
|
|
if APage <> nil then
|
|
begin
|
|
with APage^.SheetInfo.LinkToDimension^ do
|
|
begin
|
|
if IntData[2] <= AMaxRow then
|
|
IntData[2] := AMaxRow + 1;
|
|
if WordData[7] <= AMaxCol then
|
|
WordData[7] := AMaxCol + 1;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.StoreTreeNode(AStream: TStream;
|
|
ANode: PxlsTreeNode; const IsRoot: Boolean = False);
|
|
var
|
|
APos: Integer;
|
|
begin
|
|
with AStream do
|
|
begin
|
|
if IsRoot then
|
|
APos := AStream.Position
|
|
else
|
|
APos := 0;
|
|
if not IsRoot then
|
|
begin
|
|
WriteBuffer(ANode^.SheetInfo.BOF, SizeOf(ANode^.SheetInfo.BOF));
|
|
ANode := ANode^.SheetNodesList;
|
|
end;
|
|
ANode := GetRoot(ANode);
|
|
while ANode <> nil do
|
|
begin
|
|
WriteBuffer(ANode^.ByteData, ANode^.WordData[1] + 4);
|
|
ANode := ANode^.Next;
|
|
end;
|
|
if ISRoot then
|
|
FSST.SaveToStream(AStream, AStream.Position - APos);
|
|
WriteBuffer(EOF_REC, SizeOf(EOF_REC));
|
|
end;
|
|
end;
|
|
|
|
function TcxExcelFileWriter.UnionNodes(AFirstNode, ALastNode: PxlsTreeNode): PxlsTreeNode;
|
|
var
|
|
ABeginNode, AEndNode: PxlsTreeNode;
|
|
begin
|
|
ABeginNode := GetRoot(ALastNode);
|
|
AEndNode := GetEnd(ALastNode);
|
|
Result := AFirstNode;
|
|
if Result <> nil then
|
|
begin
|
|
if Result^.Next <> nil then
|
|
begin
|
|
Result^.Next^.Prev := AEndNode;
|
|
AEndNode^.Next := Result^.Next;
|
|
end;
|
|
ABeginNode^.Prev := Result;
|
|
Result^.Next := ABeginNode;
|
|
end
|
|
else
|
|
Result := ALastNode;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.CreateDefaultStyles;
|
|
|
|
procedure AddFormat(const AFormat: array of Byte);
|
|
begin
|
|
FStorage.RootHeader := CreateXlsListNode(brcFORMAT, Length(AFormat), FStorage.RootHeader);
|
|
with FStorage.RootHeader^ do
|
|
Move(AFormat[0], WordData[2], Length(AFormat));
|
|
end;
|
|
|
|
var
|
|
I: Integer;
|
|
AXFSize: Byte;
|
|
|
|
const
|
|
DDMMYY: array[0..12] of Byte =
|
|
(164, 0, 8, 0, 0, 100, 100, 47, 109, 109, 47, 121, 121);
|
|
DDMMMMYY: array[0..16] of Byte =
|
|
(165, 0, 12, 0, 0, 100, 100, 92, 46, 109, 109, 109, 109, 92, 46, 121, 121);
|
|
|
|
begin
|
|
FPalette := cxExcelStdColors;
|
|
AXFSize := SizeOf(DefaultStylesTable[0]);
|
|
AddFormat(DDMMYY);
|
|
AddFormat(DDMMMMYY);
|
|
for I := 0 to High(DefaultStylesTable) do
|
|
begin
|
|
FStorage.RootHeader := CreateXlsListNode(brcXF, AXFSize, FStorage.RootHeader);
|
|
Move(DefaultStylesTable[I], FStorage.RootHeader^.ByteData[4], AXFSize);
|
|
end;
|
|
FCurrentStylePtr := FStorage.RootHeader;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.CreateDefaultFonts;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to 3 do
|
|
begin
|
|
FStorage.RootHeader := CreateXlsListNode(brcFont,
|
|
SizeOf(DefaultFont), FStorage.RootHeader);
|
|
Move(DefaultFont, FStorage.RootHeader.ByteData[4], SizeOf(DefaultFont));
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SetBlank(ACol, ARow: Word);
|
|
begin
|
|
with CreateXlsListNode(brcBlank, 6, FStorage.Pages^.SheetNodesList)^ do
|
|
begin
|
|
WordData[2] := ARow;
|
|
WordData[3] := ACol;
|
|
WordData[4] := FCurrentStyle;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SetBoolErr(ACol, ARow: Word; ABoolValue: Boolean);
|
|
begin
|
|
with CreateXlsListNode(brcNumber, 8, FStorage.Pages^.SheetNodesList)^ do
|
|
begin
|
|
WordData[2] := ARow;
|
|
WordData[3] := ACol;
|
|
WordData[4] := FCurrentStyle;
|
|
ByteData[10] := 1;
|
|
ByteData[11] := Byte(ABoolValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SetDouble(ACol, ARow: Word; ADoubleValue: Double);
|
|
begin
|
|
with CreateXlsListNode(brcNumber, 14, FStorage.Pages^.SheetNodesList)^ do
|
|
begin
|
|
WordData[2] := ARow;
|
|
WordData[3] := ACol;
|
|
WordData[4] := FCurrentStyle;
|
|
PDouble(@ByteData[10])^ := ADoubleValue;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SetSSTString(ACol, ARow: Word; const AStringValue: WideString);
|
|
begin
|
|
with CreateXlsListNode(brcLabelSST, 10, FStorage.Pages^.SheetNodesList)^ do
|
|
begin
|
|
WordData[2] := ARow;
|
|
WordData[3] := ACol;
|
|
WordData[4] := FCurrentStyle;
|
|
WordData[5] := FSST.Add(AStringValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TcxExcelFileWriter.SetWString(ACol, ARow: Word; const AStringValue: WideString);
|
|
begin
|
|
with CreateXlsListNode(brcLabel, 9 + Length(AStringValue) * 2, FStorage.Pages^.SheetNodesList)^ do
|
|
begin
|
|
WordData[2] := ARow;
|
|
WordData[3] := ACol;
|
|
WordData[4] := FCurrentStyle;
|
|
WordData[5] := Length(AStringValue);
|
|
ByteData[12] := IsUnicodeStr;
|
|
Move(AStringValue[1], ByteData[13], Length(AStringValue) * 2);
|
|
end;
|
|
end;
|
|
|
|
{ TSSTStringTable }
|
|
constructor TSSTStringTable.Create;
|
|
begin
|
|
Clear;
|
|
end;
|
|
|
|
destructor TSSTStringTable.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TSSTStringTable.Add(const AString: WideString): Integer;
|
|
begin
|
|
Result := IndexOf(AString);
|
|
if Result = -1 then
|
|
begin
|
|
Result := UniqueStringCount;
|
|
InsertStr(AString);
|
|
Inc(PInteger(@FSST[0].Data[4])^);
|
|
end;
|
|
Inc(PInteger(@FSST[0].Data[0])^);
|
|
end;
|
|
|
|
procedure TSSTStringTable.Clear;
|
|
begin
|
|
SetLength(FSST, 0);
|
|
SetLength(FStringsInfo, 0);
|
|
FillChar(FExtSST, SizeOf(FExtSST), 0);
|
|
FExtSST.RecType := brcExtSST;
|
|
end;
|
|
|
|
procedure TSSTStringTable.SaveToStream(AStream: TStream; APosition: Integer = -1);
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if APosition = -1 then
|
|
APosition := AStream.Position;
|
|
CreateExtSST(APosition);
|
|
with AStream do
|
|
begin
|
|
for I := 0 to Length(FSST) - 1 do
|
|
if FExtSST.DataSize > 0 then
|
|
WriteBuffer(FSST[I].RecType, FSST[I].DataSize + 4);
|
|
if FExtSST.DataSize > 0 then
|
|
WriteBuffer(FExtSST.RecType, FExtSST.DataSize + 4);
|
|
end;
|
|
end;
|
|
|
|
procedure TSSTStringTable.CreateExtSST(ASSTOffset: Integer);
|
|
|
|
function GetSkipSize(ABlock: Word): Integer;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := 4;
|
|
for I := 1 to ABlock - 1 do
|
|
Result := Result + FSST[I].DataSize;
|
|
end;
|
|
|
|
var
|
|
ABlocksCount: Integer;
|
|
AStringCount: Word;
|
|
I: Integer;
|
|
begin
|
|
if Length(FSST) = 0 then Exit;
|
|
AStringCount := 8;
|
|
ABlocksCount := 1;
|
|
while (UniqueStringCount - AStringCount * ABlocksCount) > 0 do
|
|
begin
|
|
Inc(AStringCount, 8);
|
|
if ABlocksCount < 127 then
|
|
if (UniqueStringCount - AStringCount * ABlocksCount) > 0 then Inc(ABlocksCount);
|
|
end;
|
|
while ((ABlocksCount - 1)* AStringCount) > UniqueStringCount do
|
|
Dec(ABlocksCount);
|
|
FExtSST.DataSize := 2 + ABlocksCount * 8;
|
|
FExtSST.StringPerBlock := AStringCount;
|
|
for I := 0 to ABlocksCount - 1 do
|
|
with FStringsInfo[I * AStringCount] do
|
|
FExtSST.Data[I].StreamOffset := ASSTOffset + GetSkipSize(Block) + Offset;
|
|
end;
|
|
|
|
function TSSTStringTable.GetPackedSize: Integer;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := 0;
|
|
for I := 0 to Length(FSST) - 1 do
|
|
Inc(Result, FSST[I].DataSize + 4);
|
|
if Result <> 0 then
|
|
begin
|
|
CreateExtSST(0);
|
|
Inc(Result, FExtSST.DataSize + 4);
|
|
end;
|
|
end;
|
|
|
|
function TSSTStringTable.IndexOf(const AString: WideString): Integer;
|
|
|
|
function CheckString(ABlock, AOffset, ASize: Word): Boolean;
|
|
var
|
|
AStrPtr: PByteArray;
|
|
ALen: Integer;
|
|
AStrPos: Integer;
|
|
begin
|
|
AStrPtr := @AString[1];
|
|
if (ASize + AOffset) <= FSST[ABlock].DataSize then
|
|
Result := CompareMem(@FSST[ABlock].Data[AOffset], AStrPtr, ASize)
|
|
else
|
|
begin
|
|
Result := True;
|
|
AStrPos := 0;
|
|
while ASize > 0 do
|
|
begin
|
|
with FSST[ABlock] do
|
|
begin
|
|
ALen := (DataSize - AOffset);
|
|
if ALen < ASize then
|
|
begin
|
|
Result := Result and CompareMem(@Data[AOffset], @AStrPtr^[AStrPos], ALen);
|
|
Dec(ASize, ALen);
|
|
Inc(AStrPos, ALen);
|
|
AOffset := 1;
|
|
Inc(ABlock);
|
|
end
|
|
else
|
|
begin
|
|
Result := Result and CompareMem(@Data[AOffset], @AStrPtr^[AStrPos], ASize);
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
I: Integer;
|
|
ASrcLen: Word;
|
|
AHashCode: Word;
|
|
|
|
begin
|
|
ASrcLen := Length(AString);
|
|
if ASrcLen > 32768 then
|
|
ASrcLen := 32768;
|
|
Result := -1;
|
|
ASrcLen := ASrcLen shl 1;
|
|
AHashCode := cxExcelAccess.GetHashCode(Pointer(@AString[1])^, ASrcLen);
|
|
for I := 0 to UniqueStringCount - 1 do
|
|
begin
|
|
with FStringsInfo[I] do
|
|
begin
|
|
if (HashCode = AHashCode) and (ASrcLen = StrSize) then
|
|
if CheckString(Block, Offset + 3, StrSize) then
|
|
begin
|
|
Result := I;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TSSTStringTable.InsertStr(const AString: WideString);
|
|
|
|
function AddBlock: Integer;
|
|
begin
|
|
Result := Length(FSST);
|
|
SetLength(FSST, Result + 1);
|
|
FillChar(FSST[Result], SizeOf(TSSTBlock), 0);
|
|
FSST[Result].RecType := brcContinue;
|
|
end;
|
|
|
|
procedure AddStringInfo(ABlock, AOffset, ASize: Word);
|
|
var
|
|
AInfoCount: Integer;
|
|
AInfoIndex: Integer;
|
|
begin
|
|
AInfoCount := Length(FStringsInfo);
|
|
AInfoIndex := UniqueStringCount;
|
|
if AInfoCount <= UniqueStringCount then
|
|
SetLength(FStringsInfo, AInfoCount + 512);
|
|
with FStringsInfo[AInfoIndex] do
|
|
begin
|
|
HashCode := cxExcelAccess.GetHashCode(Pointer(@AString[1])^, ASize);
|
|
StrSize := ASize;
|
|
Block := ABlock;
|
|
Offset := AOffset;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
AEndBlock: SmallInt;
|
|
AStrSize: Word;
|
|
AWriteSize: Word;
|
|
AOffset: Word;
|
|
|
|
begin
|
|
AEndBlock := Length(FSST) - 1;
|
|
if AEndBlock < 0 then
|
|
begin
|
|
AEndBlock := AddBlock;
|
|
with FSST[AEndBlock] do
|
|
begin
|
|
RecType := brcSST;
|
|
DataSize := 8;
|
|
StringOffset := 8;
|
|
end;
|
|
end;
|
|
AStrSize := Length(AString);
|
|
if AStrSize > 32768 then
|
|
AStrSize := 32768;
|
|
AStrSize := AStrSize shl 1;
|
|
|
|
if (FSST[AEndBlock].DataSize + 4) > MaxBlockSize then
|
|
AEndBlock := AddBlock;
|
|
AWriteSize := MaxBlockSize - (FSST[AEndBlock].DataSize + 3);
|
|
if AWriteSize > AStrSize then
|
|
AWriteSize := AStrSize
|
|
else
|
|
begin
|
|
if (AWriteSize and $1) <> 0 then
|
|
Dec(AWriteSize);
|
|
end;
|
|
with FSST[AEndBlock] do
|
|
begin
|
|
if StringCount = 0 then
|
|
StringOffset := DataSize;
|
|
AddStringInfo(AEndBlock, DataSize, AStrSize);
|
|
Inc(StringCount);
|
|
PWord(@Data[DataSize])^ := AStrSize shr 1;
|
|
Inc(DataSize, 2);
|
|
end;
|
|
AddStringToBlock(@AString[1], FSST[AEndBlock], AWriteSize);
|
|
AOffset := 0;
|
|
while (AStrSize - AWriteSize) > 0 do
|
|
begin
|
|
Inc(AOffset, AWriteSize);
|
|
Dec(AStrSize, AWriteSize);
|
|
AEndBlock := AddBlock;
|
|
if AStrSize > (MaxBlockSize - 12) then
|
|
AWriteSize := MaxBlockSize - 12
|
|
else
|
|
AWriteSize := AStrSize;
|
|
AddStringToBlock(@PByteArray(@AString[1])^[AOffset], FSST[AEndBlock], AWriteSize);
|
|
end;
|
|
end;
|
|
|
|
function TSSTStringTable.GetStringCount: Integer;
|
|
begin
|
|
if Length(FSST) > 0 then
|
|
Result := PIntArray(@FSST[0].Data)^[0]
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
function TSSTStringTable.GetUniqueStringCount: Integer;
|
|
begin
|
|
if Length(FSST) > 0 then
|
|
Result := PIntArray(@FSST[0].Data)^[1]
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
{ TcxWorkBookReader }
|
|
procedure TcxWorkBookWriter.CreateOLEStream(ADataSize: Integer;
|
|
ADstStream: TStream);
|
|
var
|
|
ASize: Integer;
|
|
begin
|
|
FBuffer := nil;
|
|
Check((ADataSize > 0) and (ADstStream <> nil));
|
|
FStreamSize := ADataSize;
|
|
FSectCount := RoundDIV(FStreamSize, oleBigBlockSize);
|
|
ASize := RoundDIV(FSectCount, oleBlockIDPerBigBlock) + 3;
|
|
FIsSmallFile := FStreamSize < oleMiniSectorMaxSize;
|
|
if not IsSmallFile then
|
|
ReallocBuffer(oleBigBlockSize * (ASize + RoundDiv(ASize, oleMaxBlockIDInBigBlock)))
|
|
else
|
|
ReallocBuffer(4 shl oleBigBlockShift);
|
|
CreateHeader;
|
|
CreateDIR;
|
|
CreateFAT;
|
|
try
|
|
if ADstStream.InheritsFrom(TMemoryStream) then
|
|
TMemoryStream(ADstStream).SetSize(Size + FSectCount shl oleBigBlockShift);
|
|
ADstStream.WriteBuffer(FBuffer^, Size);
|
|
finally
|
|
ReallocBuffer(oleEmpty);
|
|
end;
|
|
end;
|
|
|
|
procedure TcxWorkBookWriter.Check(ACondition: Boolean);
|
|
begin
|
|
if not ACondition then
|
|
raise EcxExcelDataWriter.Create(cxGetResourceString(@scxWorkbookWrite));
|
|
end;
|
|
|
|
procedure TcxWorkBookWriter.CreateDIF;
|
|
var
|
|
I, AId: Integer;
|
|
ACurDIF: PcxFATSector;
|
|
ACurSect: Integer;
|
|
AIndex, ASectorID: Integer;
|
|
begin
|
|
AIndex := 0;
|
|
ACurSect := 0;
|
|
ACurDIF := DIF[ACurSect];
|
|
AId := 0;
|
|
with Header^ do
|
|
begin
|
|
for I := oleSectorsInMasterFAT - 1 to CountSectFAT - 2 do
|
|
begin
|
|
ASectorID := I - 108;
|
|
if AIndex = oleMaxBlockIdInBigBlock then
|
|
begin
|
|
ACurDIF^[oleMaxBlockIdInBigBlock] := ASectorID + 111 + AId;
|
|
ACurDIF := DIF[ACurSect + 1];
|
|
AIndex := 0;
|
|
Inc(ACurSect);
|
|
end;
|
|
if ((ASectorID + AId - 1) mod oleBlockIDPerBigBlock) = 0 then
|
|
Inc(AId);
|
|
ACurDIF^[AIndex] := ASectorID + 110 + AId;
|
|
Inc(AIndex);
|
|
end;
|
|
FillChar(ACurDIF^[AIndex], (oleBlockIDPerBigBlock - AIndex) * oleIndexSize, oleUnused);
|
|
end;
|
|
end;
|
|
|
|
procedure TcxWorkBookWriter.CreateDIR;
|
|
|
|
procedure CreateEntry(const AName: WideString;
|
|
AType: TcxOLEDIREntryType; AEntry: PcxOLEDIREntry);
|
|
begin
|
|
with AEntry^ do
|
|
begin
|
|
EntryType := AType;
|
|
BFlag := Byte(AType = ET_STREAM);
|
|
LeftSib := LongWord(oleUnused);
|
|
RightSib := LongWord(oleUnused);
|
|
ChildSib := LongWord(oleUnused);
|
|
if AType in [ET_STREAM, ET_ROOT] then
|
|
begin
|
|
NameLen := (Length(AName) + 1) shl 1;
|
|
if NameLen <> 1 then
|
|
Move(AName[1], Name, NameLen - 2);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
FillChar(DIR[0]^, oleBigBlockSize, oleEmpty);
|
|
CreateEntry(oleRoot, ET_ROOT, DIR[0]);
|
|
CreateEntry(oleWorkBook, ET_STREAM, DIR[1]);
|
|
DIR[0]^.ChildSib := 1;
|
|
if not IsSmallFile then
|
|
begin
|
|
DIR[0]^.StartSector := oleEndOfChain;
|
|
with Header^ do
|
|
DIR[1]^.StartSector := CountSectFAT + CountSectDIF + 1;
|
|
end
|
|
else
|
|
begin
|
|
DIR[0]^.StartSector := 3;
|
|
DIR[0]^.Size := FSectCount shl oleBigBlockShift;
|
|
end;
|
|
DIR[1]^.Size := FStreamSize;
|
|
end;
|
|
|
|
procedure TcxWorkBookWriter.CreateFAT;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
with Header^ do
|
|
begin
|
|
if not FIsSmallFile then
|
|
begin
|
|
for I := 0 to Min(CountSectFAT, oleSectorsInMasterFAT) - 1 do
|
|
begin
|
|
if I = 0 then
|
|
Header^.SectFAT[0] := 0
|
|
else
|
|
Header^.SectFAT[I] := I + 1;
|
|
end;
|
|
if CountSectDIF > 0 then
|
|
CreateDIF;
|
|
CreateLocalFAT;
|
|
end
|
|
else
|
|
begin
|
|
SectFAT[0] := 0;
|
|
CreateSmallFAT;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxWorkBookWriter.CreateHeader;
|
|
var
|
|
ACount: Integer;
|
|
begin
|
|
with Header^ do
|
|
begin
|
|
Move(oleHeaderTemplate, Header^, oleBigBlockSize - SizeOf(SectFAT));
|
|
FillChar(SectFAT, SizeOf(SectFAT), oleUnused);
|
|
if not FIsSmallFile then
|
|
begin
|
|
CountSectFAT := RoundDIV(FSectCount + 3, oleMaxBlockIdInBigBlock);
|
|
if CountSectFAT > oleSectorsInMasterFAT then
|
|
begin
|
|
ACount := CountSectFAT - oleSectorsInMasterFAT;
|
|
CountSectDIF := RoundDIV(ACount, oleMaxBlockIdInBigBlock);
|
|
SectDIFStart := oleSectorsInMasterFAT + oleReservedSectorCount;
|
|
end;
|
|
ReallocBuffer((CountSectFAT + CountSectDIF + oleReservedSectorCount) shl oleBigBlockShift);
|
|
end
|
|
else
|
|
begin
|
|
SectMiniFATStart := 2;
|
|
CountSectMiniFAT := 1;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxWorkBookWriter.CreateSmallFAT;
|
|
var
|
|
ABigFAT: PcxFATSector;
|
|
ASmallFAT: PcxFATSector;
|
|
ABlockCount: Integer;
|
|
I: Integer;
|
|
begin
|
|
ABigFAT := @FBuffer^[oleBigBlockSize];
|
|
ASmallFAT := @FBuffer^[3 shl oleBigBlockShift];
|
|
ABlockCount := RoundDiv(FStreamSize, oleSmallBlockSize);
|
|
FillChar(ABigFAT^, oleBigBlockSize, oleUnused);
|
|
ABigFAT^[0] := oleSpecBlock;
|
|
ABigFAT^[1] := oleEndOfChain;
|
|
ABigFAT^[2] := oleEndOfChain;
|
|
I := 3;
|
|
while (I - 3) < (FSectCount - 1) do
|
|
begin
|
|
ABigFAT^[I] := I + 1;
|
|
Inc(I);
|
|
end;
|
|
ABigFAT^[I] := oleEndOfChain;
|
|
for I := 0 to ABlockCount - 2 do
|
|
ASmallFAT^[I] := I + 1;
|
|
ASmallFAT^[ABlockCount - 1] := oleEndOfChain;
|
|
FillChar(ASmallFAT^[ABlockCount],
|
|
(oleBlockIDPerBigBlock - ABlockCount) * oleIndexSize, oleUnused);
|
|
end;
|
|
|
|
procedure TcxWorkBookWriter.ReallocBuffer(const ASize: Integer);
|
|
begin
|
|
FBufferSize := (RoundDIV(ASize, oleMiniSectorMaxSize) + 1) * oleMiniSectorMaxSize;
|
|
try
|
|
if FBufferSize > FCapacity then
|
|
begin
|
|
FCapacity := FBufferSize;
|
|
ReallocMem(FBuffer, FCapacity);
|
|
end
|
|
finally
|
|
if ASize = 0 then
|
|
begin
|
|
FreeMem(FBuffer, FCapacity);
|
|
FBuffer := nil;
|
|
end;
|
|
FBufferSize := ASize;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxWorkBookWriter.CreateLocalFAT;
|
|
var
|
|
I: Integer;
|
|
AIndex: Integer;
|
|
ASector: Integer;
|
|
ACurSector: PcxFATSector;
|
|
ADIF: Integer;
|
|
|
|
procedure IncCurrentIndexAndSetValue(const Value: Integer);
|
|
begin
|
|
ACurSector^[AIndex] := Value;
|
|
if AIndex = oleMaxBlockIdInBigBlock then
|
|
begin
|
|
Inc(ASector);
|
|
ACurSector := FAT[ASector];
|
|
AIndex := 0;
|
|
end
|
|
else
|
|
Inc(AIndex);
|
|
end;
|
|
|
|
begin
|
|
AIndex := 0;
|
|
ASector := 0;
|
|
ADIF := 0;
|
|
ACurSector := FAT[ASector];
|
|
IncCurrentIndexAndSetValue(oleSpecBlock);
|
|
IncCurrentIndexAndSetValue(oleEndOfChain);
|
|
with Header^ do
|
|
begin
|
|
for I := 1 to CountSectFAT + CountSectDIF - 1 do
|
|
begin
|
|
if CountSectDIF > 0 then
|
|
if (ADIF + SectDIFStart - 1) = I then
|
|
begin
|
|
Inc(ADIF, oleBlockIDPerBigBlock);
|
|
IncCurrentIndexAndSetValue(oleDIFBlock);
|
|
Continue;
|
|
end;
|
|
IncCurrentIndexAndSetValue(oleSpecBlock);
|
|
end;
|
|
end;
|
|
with DIR[1]^ do
|
|
for I := StartSector + 1 to StartSector + FSectCount - 1 do
|
|
IncCurrentIndexAndSetValue(I);
|
|
IncCurrentIndexAndSetValue(oleEndOfChain);
|
|
if AIndex <> 0 then
|
|
begin
|
|
I := oleBlockIDPerBigBlock - AIndex;
|
|
if I > 0 then
|
|
FillChar(ACurSector^[AIndex], I * 4, oleUnused);
|
|
end;
|
|
end;
|
|
|
|
function TcxWorkBookWriter.GetDIFSector(ASector: Integer): PcxFATSector;
|
|
begin
|
|
Result := @PcxFATSectors(FBuffer)^[Header^.SectDIFStart +
|
|
(ASector * oleBlockIDPerBigBlock) + 1];
|
|
end;
|
|
|
|
function TcxWorkBookWriter.GetDIREntry(AIndex: Integer): PcxOLEDIREntry;
|
|
begin
|
|
Result := @FBuffer[oleReservedSectorCount shl oleBigBlockShift +
|
|
AIndex * oleDIRBlockSize];
|
|
end;
|
|
|
|
function TcxWorkBookWriter.GetHeader: PcxOleFileHeader;
|
|
begin
|
|
Result := PcxOleFileHeader(FBuffer);
|
|
end;
|
|
|
|
function TcxWorkBookWriter.GetFATSector(ASector: Integer): PcxFATSector;
|
|
var
|
|
ADIFBlock: Integer;
|
|
begin
|
|
if ASector = 0 then
|
|
Result := @PcxFATSectors(FBuffer)^[1]
|
|
else
|
|
begin
|
|
if ASector < oleSectorsInMasterFAT then
|
|
Result := @PcxFATSectors(FBuffer)^[ASector + 2]
|
|
else
|
|
begin
|
|
ASector := ASector - oleSectorsInMasterFAT;
|
|
ADIFBlock := 0;
|
|
while (ASector - oleMaxBlockIdInBigBlock) >= 0 do
|
|
begin
|
|
Dec(ASector, oleMaxBlockIdInBigBlock);
|
|
Inc(ADIFBlock);
|
|
end;
|
|
Result := @PcxFATSectors(FBuffer)^[DIF[ADIFBlock]^[ASector] + 1];
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ TcxWorkBookReader }
|
|
constructor TcxWorkBookReader.Create(AStream: TStream);
|
|
begin
|
|
FStream := AStream;
|
|
FCurrentDIF := -1;
|
|
FCurrentFAT := -1;
|
|
FBuffer := nil;
|
|
ReadWorkBookStream;
|
|
end;
|
|
|
|
destructor TcxWorkBookReader.Destroy;
|
|
begin
|
|
try
|
|
FreeMem(FBuffer);
|
|
finally
|
|
inherited Destroy;
|
|
end;
|
|
end;
|
|
|
|
procedure TcxWorkBookReader.CreateStreamSectorChain;
|
|
var
|
|
AIndex: Integer;
|
|
IsBreak: Boolean;
|
|
AValue: Integer;
|
|
|
|
procedure AddItemToChain(AItem: Integer);
|
|
begin
|
|
if not IsSpecialSector(AItem) then
|
|
begin
|
|
AValue := AItem;
|
|
if not IsSmallFile then Inc(AItem);
|
|
FLinSect[AIndex] := AItem;
|
|
Inc(AIndex);
|
|
end
|
|
else
|
|
Inc(AValue);
|
|
IsBreak := AIndex >= FSectCount;
|
|
end;
|
|
|
|
begin
|
|
AIndex := 0;
|
|
SetLength(FLinSect, FSectCount);
|
|
AddItemToChain(DIR[FWorkBookDIR]^.StartSector);
|
|
while not IsBreak do
|
|
begin
|
|
if not IsSmallFile then
|
|
AddItemToChain(FAT[AValue div oleBlockIDPerBigBlock]^[AValue mod oleBlockIDPerBigBlock])
|
|
else
|
|
AddItemToChain(SmallFAT[0][AValue]);
|
|
end;
|
|
end;
|
|
|
|
function TcxWorkBookReader.IsSpecialSector(ASector: Integer): Boolean;
|
|
begin
|
|
Result := (ASector = oleDIFBlock) or (ASector = oleSpecBlock) or
|
|
(ASector = oleEndOfChain) or (ASector = oleUnused);
|
|
end;
|
|
|
|
procedure TcxWorkBookReader.ReadBuffer(var ABuf;
|
|
const ASize: Integer; const APos: Integer);
|
|
var
|
|
ACount: Integer;
|
|
begin
|
|
if APos >= 0 then
|
|
begin
|
|
FIsError := not (APos < FStream.Size);
|
|
if not FIsError then
|
|
FStream.Seek(APos, soFromBeginning)
|
|
else
|
|
Exit;
|
|
end;
|
|
ACount := FStream.Read(ABuf, ASize);
|
|
if ACount <> ASize then
|
|
FillChar(PByteArray(@ABuf)^[ACount], (ASize - ACount) * SizeOf(Integer), oleEmpty);
|
|
end;
|
|
|
|
procedure TcxWorkBookReader.ReadStreamData;
|
|
var
|
|
AData: Pointer;
|
|
APos: Integer;
|
|
I: Integer;
|
|
begin
|
|
AData := FBuffer;
|
|
for I := 0 to FSectCount - 1 do
|
|
begin
|
|
if IsSmallFile then
|
|
APos := $0800 + FLinSect[I] shl oleSmallBlockShift
|
|
else
|
|
APos := FLinSect[I] shl oleBigBlockShift;
|
|
ReadBuffer(AData^, SectorSize[IsSmallFile], APos);
|
|
if not FIsError then
|
|
Inc(Integer(AData), SectorSize[IsSmallFile]);
|
|
end;
|
|
end;
|
|
|
|
procedure TcxWorkBookReader.ReadWorkBookStream;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
FWorkBookDIR := -1;
|
|
GetSector(-1, TcxFATSector(FHeader));
|
|
if FIsError then Exit;
|
|
GetSector(Header.SectDIRStart, TcxFATSector(FDIRSector));
|
|
if FIsError then Exit;
|
|
for I := 0 to 3 do
|
|
with DIR[I]^ do
|
|
begin
|
|
if (((NameLen shr 1) - 1) = Length(oleWorkbook)) and
|
|
CompareMem(@Name[0], @oleWorkbook[1], NameLen - 2) then
|
|
begin
|
|
FWorkBookDIR := I;
|
|
FIsSmallFile := Size < oleMiniSectorMaxSize;
|
|
FSectCount := RoundDIV(Size, SectorSize[IsSmallFile]);
|
|
FBuffer := AllocMem((FSectCount + 10) * SectorSize[IsSmallFile]);
|
|
Break;
|
|
end;
|
|
end;
|
|
FIsError := not ((FWorkBookDIR >= 0) and (Header.Signature = oleSignature));
|
|
if not FIsError then
|
|
begin
|
|
CreateStreamSectorChain;
|
|
ReadStreamData;
|
|
end;
|
|
if FIsError then
|
|
begin
|
|
FreeMem(FBuffer);
|
|
FBuffer := nil;
|
|
end;
|
|
end;
|
|
|
|
function TcxWorkBookReader.GetBufferSize: Integer;
|
|
begin
|
|
Result := FDIRSector[FWorkBookDIR].Size;
|
|
end;
|
|
|
|
function TcxWorkBookReader.GetDIFSector(ASector: Integer): PcxFATSector;
|
|
var
|
|
ADIFStart: Integer;
|
|
I: Integer;
|
|
const
|
|
DIFSectorPos = oleMaxBlockIdInBigBlock * SizeOf(Integer);
|
|
begin
|
|
Result := nil;
|
|
if ASector <> FCurrentDIF then
|
|
begin
|
|
FCurrentDIF := ASector;
|
|
ADIFStart := Header.SectDIFStart;
|
|
for I := 0 to ASector - 1 do
|
|
begin
|
|
if FIsError then Exit;
|
|
ReadBuffer(ADIFStart, SizeOf(ADIFStart),
|
|
(ADIFStart + 1) shl oleBigBlockShift + DIFSectorPos);
|
|
end;
|
|
GetSector(ADIFStart, FDIF);
|
|
end;
|
|
Result := @FDIF;
|
|
end;
|
|
|
|
function TcxWorkBookReader.GetDIREntry(AEntry: Integer): PcxOLEDIREntry;
|
|
begin
|
|
Result := @FDIRSector[AEntry]
|
|
end;
|
|
|
|
function TcxWorkBookReader.GetFATSector(ASector: Integer): PcxFATSector;
|
|
begin
|
|
Result := nil;
|
|
if ASector >= oleSectorsInMasterFAT then
|
|
begin
|
|
ASector := ASector - oleSectorsInMasterFAT;
|
|
ASector := DIF[ASector div oleMaxBlockIdInBigBlock]^[ASector mod oleMaxBlockIdInBigBlock];
|
|
end
|
|
else
|
|
ASector := Header.SectFAT[ASector];
|
|
if ASector <> FCurrentFAT then
|
|
begin
|
|
FCurrentFAT := ASector;
|
|
GetSector(ASector, FFAT);
|
|
if FIsError then Exit;
|
|
end;
|
|
Result := @FFAT;
|
|
end;
|
|
|
|
procedure TcxWorkBookReader.GetSector(ASector: Integer; var AData: TcxFATSector);
|
|
begin
|
|
ReadBuffer(AData, oleBigBlockSize, (ASector + 1) shl oleBigBlockShift);
|
|
end;
|
|
|
|
function TcxWorkBookReader.GetSmallFATSector(ASector: Integer): TcxFATSector;
|
|
begin
|
|
GetSector(Header.SectMiniFATStart + ASector, Result);
|
|
end;
|
|
|
|
var
|
|
DC: HDC;
|
|
|
|
initialization
|
|
DC := GetDC(0);
|
|
try
|
|
ScreenResolution := GetDeviceCaps(DC, LOGPIXELSY);
|
|
ScaledFactor := ScreenResolution / 96;
|
|
finally
|
|
ReleaseDC(0, DC)
|
|
end;
|
|
|
|
finalization
|
|
|
|
end.
|
|
|