📄 uxlsescher.pas
字号:
unit UXlsEscher;
{$IFDEF LINUX}{$INCLUDE ../FLXCOMPILER.INC}{$ELSE}{$INCLUDE ..\FLXCOMPILER.INC}{$ENDIF}
interface
uses UXlsBaseRecords, UXlsBaseRecordLists, UXlsOtherRecords,
XlsMessages, UFlxMessages, Classes, SysUtils, UEscherRecords, UXlsSST, UBreakList,
UEscherOtherRecords;
type
TXlsEscherRecord = class (TBaseRecord)
end;
TDrawingGroupRecord = class (TXlsEscherRecord)
end;
TDrawingRecord = class (TXlsEscherRecord)
end;
TDrawingSelectionRecord = class (TXlsEscherRecord)
end;
TDrawingGroup= class
private
FDggContainer: TEscherContainerRecord;
FRecordCache: TEscherDwgGroupCache;
function GetRecordCache: PEscherDwgGroupCache;
public
property RecordCache: PEscherDwgGroupCache read GetRecordCache;
constructor Create;
procedure Clear;
destructor Destroy; override;
procedure LoadFromStream(const DataStream: TStream; const First: TDrawingGroupRecord);
procedure SaveToStream(const DataStream: TStream);
function TotalSize: int64;
procedure AddDwg;
procedure EnsureDwgGroup;
end;
TDrawing=class
private
FDgContainer: TEscherContainerRecord;
FRecordCache: TEscherDwgCache;
FDrawingGroup: TDrawingGroup;
function GetDrawingName(index: integer): widestring;
function GetDrawingRow(index: integer): integer;
procedure CreateBasicDrawingInfo;
public
procedure Clear;
constructor Create(const aDrawingGroup: TDrawingGroup);
destructor Destroy; override;
procedure CopyFrom(const aDrawing: TDrawing);
procedure LoadFromStream(const DataStream: TStream; const First: TDrawingRecord; const SST: TSST);
procedure SaveToStream(const DataStream: TStream);
function TotalSize: int64;
procedure ArrangeInsertRowsAndCols(const aRowPos, aRowCount, aColPos, aColCount: integer; const SheetInfo: TSheetInfo);
procedure ArrangeCopySheet(const SheetInfo: TSheetInfo);
procedure InsertAndCopyRowsAndCols(const FirstRow, LastRow, DestRow, RowCount, FirstCol, LastCol, DestCol, ColCount: integer; const SheetInfo: TSheetInfo);
procedure DeleteRows(const aRow, aCount: word;const SheetInfo: TSheetInfo);
procedure DeleteCols(const aCol, aCount: word;const SheetInfo: TSheetInfo);
function FindObjId(const ObjId: word): TEscherClientDataRecord;
function DrawingCount: integer;
procedure AssignDrawing(const Index: integer; const Data: string; const DataType: TXlsImgTypes);
function GetAnchor(const Index: integer): TClientAnchor;
procedure SetAnchor(const Index: integer; const aAnchor: TClientAnchor);
procedure GetDrawingFromStream(const Index: integer; const Data: TStream; var DataType: TXlsImgTypes);
property DrawingRow[index: integer]: integer read GetDrawingRow;
property DrawingName[index: integer]: widestring read GetDrawingName;
procedure DeleteImage(const Index: integer);
procedure ClearImage(const Index: integer);
procedure AddImage(Data: string; DataType: TXlsImgTypes; const Properties: TImageProperties;const Anchor: TFlxAnchorType);
function AddNewComment(const Properties: TImageProperties): TEscherClientDataRecord;
end;
implementation
uses UXlsBaseClientData, UXlsClientData;
const
EmptyBmp= #$28#$0#$0#$0#$1#$0+
#$0#$0#$1#$0#$0#$0#$1#$0#$1#$0#$0#$0#$0#$0#$0#$0#$0#$0#$12#$B#$0+
#$0#$12#$B#$0#$0#$0#$0#$0#$0#$0#$0#$0#$0#$FF#$FF#$FF#$0#$0#$0+
#$0#$0#$0#$0#$0#$0#$0#$0;
{ TDrawingGroup }
procedure TDrawingGroup.AddDwg;
begin
if FRecordCache.Dgg<>nil then inc(FRecordCache.Dgg.FDgg.DwgSaved);
//PENDING: fix sheets
end;
procedure TDrawingGroup.Clear;
begin
FreeAndNil(FDggContainer);
end;
constructor TDrawingGroup.Create;
begin
inherited Create;
end;
destructor TDrawingGroup.Destroy;
begin
Clear;
inherited;
end;
procedure TDrawingGroup.EnsureDwgGroup;
const
DwgCache: TEscherDwgCache= ( MaxObjId:0; Dg: nil; Solver: nil; Patriarch:nil; Anchor: nil; Shape: nil; Obj: nil; Blip: nil);
var
EscherHeader: TEscherRecordHeader;
FDgg: TEscherDggRecord;
BStoreContainer: TEscherBStoreRecord;
OPTRec:TEscherOPTRecord;
SplitMenu: TEscherSplitMenuRecord;
begin
if FDggContainer=nil then // there is already a DwgGroup
begin
//DggContainer
EscherHeader.Pre:=$F;
EscherHeader.Id:=MsofbtDggContainer;
EscherHeader.Size:=0;
FDggContainer:=TEscherContainerRecord.Create(EscherHeader, RecordCache, @DwgCache ,nil);
FDggContainer.LoadedDataSize:=EscherHeader.Size;
end;
if FDggContainer.FindRec(TEscherDggRecord)=nil then
begin
//Dgg
FDgg:=TEscherDggRecord.CreateFromData(RecordCache, @DwgCache ,FDggContainer);
FDggContainer.ContainedRecords.Add(FDgg);
end;
if FDggContainer.FindRec(TEscherBStoreRecord)=nil then
begin
// BStoreContainer
EscherHeader.Pre:=$2F;
EscherHeader.Id:=MsofbtBstoreContainer;
EscherHeader.Size:=0;
BStoreContainer:=TEscherBStoreRecord.Create(EscherHeader, RecordCache, @DwgCache ,FDggContainer);
BStoreContainer.LoadedDataSize:=EscherHeader.Size;
FDggContainer.ContainedRecords.Add(BStoreContainer);
end;
if FDggContainer.FindRec(TEscherOPTRecord)=nil then
begin
//OPT
OPTRec:=TEscherOPTRecord.GroupCreateFromData(RecordCache, @DwgCache, FDggContainer);
FDggContainer.ContainedRecords.Add(OPTRec);
end;
if FDggContainer.FindRec(TEscherSplitMenuRecord)=nil then
begin
//SplitMenuColors
SplitMenu:=TEscherSplitMenuRecord.CreateFromData(RecordCache, @DwgCache, FDggContainer);
FDggContainer.ContainedRecords.Add(SplitMenu);
end;
end;
function TDrawingGroup.GetRecordCache: PEscherDwgGroupCache;
begin
Result:=@FRecordCache;
end;
procedure TDrawingGroup.LoadFromStream(const DataStream: TStream; const First: TDrawingGroupRecord);
const
DwgCache: TEscherDwgCache= ( MaxObjId:0; Dg: nil; Solver: nil; Patriarch:nil; Anchor: nil; Shape: nil; Obj: nil; Blip: nil);
var
aPos: integer;
EscherHeader: TEscherRecordHeader;
RecordHeader: TRecordHeader;
MyRecord, CurrentRecord: TBaseRecord;
begin
if FDggContainer<>nil then raise Exception.Create(ErrExcelInvalid);
aPos:=0;
MyRecord:= First; CurrentRecord:= First;
try
ReadMem(MyRecord, aPos, SizeOf(EscherHeader), @EscherHeader);
FDggContainer:= TEscherContainerRecord.Create(EscherHeader, RecordCache, @DwgCache ,nil);
while not FDggContainer.Loaded do
begin
if (MyRecord.Continue=nil) and (aPos=MyRecord.DataSize) then
begin
if CurrentRecord<> First then FreeAndNil(CurrentRecord);
if (DataStream.Read(RecordHeader, sizeof(RecordHeader)) <> sizeof(RecordHeader)) then
raise Exception.Create(ErrExcelInvalid);
CurrentRecord:=LoadRecord(DataStream, RecordHeader);
MyRecord:= CurrentRecord;
aPos:=0;
if not(MyRecord is TDrawingGroupRecord) then raise Exception.Create(ErrExcelInvalid);
end;
FDggContainer.Load(MyRecord, aPos);
end; //while
finally
if CurrentRecord<>First then FreeAndNil(CurrentRecord);
end; //finally
First.Free; //last statment
end;
procedure TDrawingGroup.SaveToStream(const DataStream: TStream);
var
BreakList: TBreakList;
NextPos, RealSize, NewDwg: integer;
begin
if FDggContainer=nil then exit;
BreakList:= TBreakList.Create(DataStream.Position);
try
NextPos:=0;
RealSize:=0;
NewDwg:= xlr_MSODRAWINGGROUP;
FDggContainer.SplitRecords(NextPos, RealSize, NewDwg, BreakList);
BreakList.Add(0, NextPos);
FDggContainer.SaveToStream(DataStream, BreakList);
finally
FreeAndNil(BreakList);
end; //finally
end;
function TDrawingGroup.TotalSize: int64;
var
NextPos, RealSize, NewDwg: integer;
begin
if FDggContainer=nil then begin Result:=0; exit;end;
NextPos:=0; RealSize:=0; NewDwg:= xlr_MSODRAWINGGROUP;
FDggContainer.SplitRecords(NextPos, RealSize, NewDwg, nil);
Result:=RealSize;
end;
{ TDrawing }
procedure TDrawing.ArrangeCopySheet(const SheetInfo: TSheetInfo);
begin
if (FRecordCache.Obj<> nil) then
FRecordCache.Obj.ArrangeCopySheet(SheetInfo);
end;
procedure TDrawing.ArrangeInsertRowsAndCols(const aRowPos, aRowCount, aColPos, aColCount: integer; const SheetInfo: TSheetInfo);
begin
if (FRecordCache.Anchor<> nil) and (SheetInfo.FormulaSheet= SheetInfo.InsSheet)then
FRecordCache.Anchor.ArrangeInsertRowsAndCols(aRowPos, aRowCount, aColPos, aColCount, SheetInfo, false);
if (FRecordCache.Obj<> nil) then
FRecordCache.Obj.ArrangeInsertRowsAndCols(aRowPos, aRowCount, aColPos, aColCount, SheetInfo, false);
end;
procedure TDrawing.AssignDrawing(const Index: integer; const Data: string;
const DataType: TXlsImgTypes);
begin
if Data='' then ClearImage(Index) //XP crashes with a 0 byte image.
else FRecordCache.Blip[Index].ReplaceImg(Data, DataType);
end;
procedure TDrawing.DeleteImage(const Index: integer);
begin
if FRecordcache.Blip=nil then exit;
if (FRecordCache.Patriarch=nil) then raise Exception.Create(ErrLoadingEscher);
FRecordCache.Patriarch.ContainedRecords.Remove(FRecordCache.Blip[Index].FindRoot);
end;
procedure TDrawing.ClearImage(const Index: integer);
begin
FRecordCache.Blip[Index].ReplaceImg(EmptyBmp, xli_Bmp);
end;
procedure TDrawing.Clear;
begin
FreeAndNil(FDgContainer);
//Order is important... Cache should be freed after DgContainer
FreeAndNil(FRecordCache.Anchor);
FreeAndNil(FRecordCache.Obj);
FreeAndNil(FRecordCache.Shape);
FreeAndNil(FRecordCache.Blip);
end;
procedure TDrawing.CopyFrom(const aDrawing: TDrawing);
begin
Clear;
FRecordCache.MaxObjId:=0;
FRecordCache.Dg:=nil; FRecordCache.Patriarch:=nil;
if aDrawing.FRecordCache.Anchor<>nil then
begin
FRecordCache.Anchor:= TEscherAnchorCache.Create;
FRecordCache.Obj:= TEscherObjCache.Create;
FRecordCache.Shape:= TEscherShapeCache.Create;
FRecordCache.Blip:=TEscherOPTCache.Create;
end;
if aDrawing.FDgContainer=nil then FreeAndNil(FDgcontainer) else
begin
aDrawing.FDgContainer.ClearCopiedTo;
FDgContainer:=aDrawing.FDgContainer.CopyTo(@FRecordCache, 0,0) as TEscherContainerRecord;
FRecordCache.Shape.Sort; // only here the values are loaded...
if FRecordCache.Solver<>nil then FRecordCache.Solver.CheckMax(aDrawing.FRecordCache.Solver.MaxRuleId);
FDrawingGroup.AddDwg;
end;
//MADE: change cache
end;
constructor TDrawing.Create(const aDrawingGroup: TDrawingGroup);
begin
inherited Create;
FDrawingGroup:=aDrawingGroup;
FRecordCache.Destroying:=false;
end;
procedure TDrawing.DeleteRows(const aRow, aCount: word;
const SheetInfo: TSheetInfo);
var i: integer;
begin
//MADE: delete rows
//MADE: Arreglar los continues...
//MADE: Conectores
if FRecordCache.Anchor=nil then exit;
for i:= FRecordCache.Anchor.Count-1 downto 0 do
if FRecordCache.Anchor[i].AllowDelete(aRow, aRow+aCount-1,0,Max_Columns+1)then
begin
if (FRecordCache.Patriarch=nil) then raise Exception.Create(ErrLoadingEscher);
FRecordCache.Patriarch.ContainedRecords.Remove(FRecordCache.Anchor[i].FindRoot);
end;
ArrangeInsertRowsAndCols(aRow, -aCount, 0,0, SheetInfo);
end;
procedure TDrawing.DeleteCols(const aCol, aCount: word;
const SheetInfo: TSheetInfo);
var i: integer;
begin
//MADE: delete cols
//MADE: Arreglar los continues...
//MADE: Conectores
if FRecordCache.Anchor=nil then exit;
for i:= FRecordCache.Anchor.Count-1 downto 0 do
if FRecordCache.Anchor[i].AllowDelete(0, Max_Rows+1, aCol, aCol+aCount-1)then
begin
if (FRecordCache.Patriarch=nil) then raise Exception.Create(ErrLoadingEscher);
FRecordCache.Patriarch.ContainedRecords.Remove(FRecordCache.Anchor[i].FindRoot);
end;
ArrangeInsertRowsAndCols(0,0,aCol, -aCount, SheetInfo);
end;
destructor TDrawing.Destroy;
begin
FRecordCache.Destroying:=true;
Clear;
inherited;
end;
function TDrawing.DrawingCount: integer;
begin
if FRecordCache.Blip<>nil then Result:=FRecordCache.Blip.Count else Result:=0;
end;
function TDrawing.FindObjId(const ObjId: word): TEscherClientDataRecord;
var
i: integer;
begin
for i:=0 to FRecordCache.Obj.Count-1 do if FRecordCache.Obj[i].ObjId=ObjId then
begin
Result:=FRecordCache.Obj[i];
exit;
end;
Result:=nil;
end;
function TDrawing.GetAnchor(const Index: integer): TClientAnchor;
begin
Assert(Index<FRecordCache.Blip.Count,'Index out of range');
Result:=FRecordCache.Blip[index].GetAnchor;
end;
procedure TDrawing.SetAnchor(const Index: integer; const aAnchor: TClientAnchor);
begin
Assert(Index<FRecordCache.Blip.Count,'Index out of range');
FRecordCache.Blip[index].SetAnchor(aAnchor);
end;
procedure TDrawing.GetDrawingFromStream(const Index: integer; const Data: TStream; var DataType: TXlsImgTypes);
begin
Assert(Index<FRecordCache.Blip.Count,'Index out of range');
FRecordCache.Blip[index].GetImageFromStream(Data, DataType);
end;
function TDrawing.GetDrawingName(index: integer): widestring;
begin
Assert(Index<FRecordCache.Blip.Count,'Index out of range');
Result:=FRecordCache.Blip[index].ShapeName;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -