📄 uxlsbaserecords.pas
字号:
if l<0 then raise Exception.Create(ErrReadingRecord);
if (l=0) and (aSize>0) then
begin
aPos:=0;
aRecord:=aRecord.Continue;
if aRecord=nil then raise Exception.Create(ErrReadingRecord);
end;
l:= aRecord.DataSize-aPos;
if aSize<=l then
begin
if pResult<>nil then Move(aRecord.Data^[aPos], pResult^, aSize);
inc(aPos, aSize);
end else
begin
ReadMem(aRecord, aPos, l, pResult);
if pResult<>nil then ReadMem(aRecord, aPos, aSize-l, PCHAR(pResult)+ l)
else ReadMem(aRecord, aPos, aSize-l, nil);
end
end;
procedure ReadStr(var aRecord: TBaseRecord; var aPos: integer; var ShortData: string; var WideData: WideString; var OptionFlags, ActualOptionFlags: byte; var DestPos: integer; const StrLen: integer );
//Read a string taking in count "Continue" Records
var
l,i: integer;
pResult: pointer;
aSize, CharSize: integer;
begin
l:= aRecord.DataSize-aPos;
if l<0 then raise Exception.Create(ErrReadingRecord);
if (l=0) and (StrLen>0) then
// This is not a valid Excel thing, but if it is (f.i. on JET exported files), the optionflags will be repeated.
{if DestPos=0 then //we are beginning the record
begin
aPos:=0;
if aRecord.Continue=nil then raise Exception.Create(ErrReadingRecord);
aRecord:=aRecord.Continue;
end else }
begin //We are in the middle of a string
aPos:=1;
if aRecord.Continue=nil then raise Exception.Create(ErrReadingRecord);
aRecord:=aRecord.Continue;
ActualOptionFlags:=aRecord.Data[0];
if (ActualOptionFlags=1) and ((OptionFlags and 1)=0 ) then
begin
WideData:=StringToWideStringNoCodePage(ShortData);
OptionFlags:= OptionFlags or 1;
end;
end;
l:= aRecord.DataSize-aPos;
if (ActualOptionFlags and 1)=0 then
begin
aSize:= StrLen-DestPos;
pResult:= @ShortData[DestPos+1];
CharSize:=1;
end else
begin
aSize:= (StrLen-DestPos)*2;
pResult:= @WideData[DestPos+1];
CharSize:=2;
end;
if aSize<=l then
begin
if (ActualOptionFlags and 1=0) and (OptionFlags and 1=1) then
//We have to move result to widedata
for i:=0 to aSize div CharSize -1 do WideData[DestPos+1+i]:=WideChar(aRecord.Data^[aPos+i])
//We are either reading widedata or shortdata
else Move(aRecord.Data^[aPos], pResult^, aSize);
inc(aPos, aSize);
inc(DestPos, aSize div CharSize);
end else
begin
if (ActualOptionFlags and 1=0) and (OptionFlags and 1=1) then
//We have to move result to widedata
for i:=0 to l div CharSize -1 do WideData[DestPos+1+i]:=WideChar(aRecord.Data^[aPos+i])
//We are either reading widedata or shortdata
else Move(aRecord.Data^[aPos], pResult^, l);
inc(aPos, l);
inc(DestPos, l div CharSize);
ReadStr(aRecord, aPos, ShortData, WideData, OptionFlags, ActualOptionFlags, DestPos ,StrLen);
end
end;
function LoadRecord(const DataStream: TStream; const RecordHeader: TRecordHeader): TBaseRecord;
var
Data: PArrayOfByte;
R: TBaseRecord;
NextRecordHeader: TRecordHeader;
begin
GetMem(Data, RecordHeader.Size);
try
if DataStream.Read(Data^, RecordHeader.Size) <> RecordHeader.Size then
raise Exception.Create(ErrExcelInvalid);
except
FreeMem(Data);
raise;
end; //except
//From here, if there is an exception, the mem will be freed by the object
case RecordHeader.Id of
xlr_BOF : R:= TBOFRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_EOF : R:= TEOFRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_FORMULA : R:= TFormulaRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_SHRFMLA : R:= TShrFmlaRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_OBJ : R:= TObjRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_MSODRAWING : R:= TDrawingRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_MSODRAWINGGROUP
: R:= TDrawingGroupRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_TXO : R:= TTXORecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_NOTE : R:= TNoteRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_RECALCID, //So the workbook gets recalculated
xlr_EXTSST, // We will have to generate this again
xlr_DBCELL, //To find rows in blocks... we need to calculate it again
xlr_INDEX, //Same as DBCELL
xlr_MSODRAWINGSELECTION // Object selection. We do not need to select any drawing
// ,xlr_OBPROJ If we want to disable macros.
: R:= TIgnoreRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_DIMENSIONS //Used range of a sheet
: R:= TDimensionsRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_SST : R:= TSSTRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_BoundSheet : R:= TBoundSheetRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_CODENAME : R:= TCodeNameRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_OBPROJ : R:= TObProjRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_Array : R:= TArrayRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_Blank : R:= TBlankRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_BoolErr : R:= TBoolErrRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_Number : R:= TNumberRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_MulBlank : R:= TMulBlankRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_MulRK : R:= TMulRKRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_RK : R:= TRKRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_STRING : R:= TStringRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);//String record saves the result of a formula
xlr_XF : R:= TXFRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_FONT : R:= TFontRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_FORMAT : R:= TFormatRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_Palette : R:= TPaletteRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_Style : R:= TStyleRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_LabelSST : R:= TLabelSSTRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_Label : R:= TLabelRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_Row : R:= TRowRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_NAME : R:= TNameRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_TABLE : R:= TTableRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_CELLMERGING : R:= TCellMergingRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_CONDFMT : R:= TCondFmtRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_CF : R:= TCFRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_DVAL : R:= TDValRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_HLINK : R:= THLinkRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_SCREENTIP : R:= TScreenTipRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_Continue : R:= TContinueRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_FOOTER : R:= TPageFooterRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_HEADER : R:= TPageHeaderRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_PRINTGRIDLINES : R:= TPrintGridLinesRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_LEFTMARGIN,
xlr_RIGHTMARGIN,
xlr_TOPMARGIN,
xlr_BOTTOMMARGIN: R:= TMarginRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_SETUP : R:= TSetupRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_PLS : R:= TPlsRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_WSBOOL : R:= TWsBoolRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_XCT, // Cached values of a external workbook... not supported yet
xlr_CRN // Cached values also
: R:=TIgnoreRecord.Create(RecordHeader.Id, Data, RecordHeader.Size); //raise Exception.Create (ErrExtRefsNotSupported);
xlr_SUPBOOK : R:= TSupBookRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_EXTERNSHEET : R:= TExternSheetRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_EXTERNNAME,
xlr_EXTERNNAME2
: R:= TExternNameRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_ChartAI : R:= TChartAIRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_Window1 : R:= TWindow1Record.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_Window2 : R:= TWindow2Record.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_1904 : R:= T1904Record.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_REFMODE : R:= TRefModeRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_PRECISION : R:= TPrecisionRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_BOOKBOOL : R:= TBookBoolRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_PANE : R:= TPaneRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_SCL : R:= TSCLRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_GUTS : R:= TGutsRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_HORIZONTALPAGEBREAKS: R:= THPageBreakRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_VERTICALPAGEBREAKS : R:= TVPageBreakRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_COLINFO : R:= TColInfoRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_DEFCOLWIDTH : R:= TDefColWidthRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_STANDARDWIDTH:R:= TStandardWidthRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_DEFAULTROWHEIGHT: R:= TDefRowHeightRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
xlr_FILEPASS: raise Exception.Create(ErrFileIsPasswordProtected);
xlr_ChartFbi: R:=TIgnoreRecord.Create(RecordHeader.Id, Data, RecordHeader.Size); //charfbi might be dangerous if copied.
else R:= TBaseRecord.Create(RecordHeader.Id, Data, RecordHeader.Size);
end; //case
//Peek at the next record...
if DataStream.Read(NextRecordHeader, SizeOf(NextRecordHeader))= SizeOf(NextRecordHeader) then
begin
if NextRecordHeader.Id = xlr_Continue then R.AddContinue(LoadRecord(DataStream, NextRecordHeader) as TContinueRecord)
else if NextRecordHeader.Id=xlr_Table then
if (R is TFormulaRecord) then
begin
(R as TFormulaRecord).TableRecord:=LoadRecord(DataStream, NextRecordHeader) as TTableRecord;
end
else Exception.Create(ErrExcelInvalid)
else if NextRecordHeader.Id=xlr_Array then
if (R is TFormulaRecord) then
begin
(R as TFormulaRecord).ArrayRecord:=LoadRecord(DataStream, NextRecordHeader) as TArrayRecord;
end
else Exception.Create(ErrExcelInvalid)
else if NextRecordHeader.Id=xlr_ScreenTip then
if (R is THLinkRecord) then
begin
(R as THLinkRecord).AddHint(LoadRecord(DataStream, NextRecordHeader) as TScreenTipRecord);
end
else Exception.Create(ErrExcelInvalid)
else
begin
if NextRecordHeader.Id = xlr_String then
begin
if not (R is TFormulaRecord) and not (R is TShrFmlaRecord) and not (R is TArrayRecord) and not (R is TTableRecord) then raise Exception.Create(ErrExcelInvalid);
end;
DataStream.Seek(-SizeOf(NextRecordHeader),soFromCurrent);
end;
end;
Result:=R;
end;
{ TBaseRecord }
procedure TBaseRecord.AddContinue(const aContinue: TContinueRecord);
begin
if Continue<>nil then raise Exception.Create(ErrInvalidContinue);
Continue:=aContinue;
end;
function TBaseRecord.CopyTo: TBaseRecord;
begin
if Self=nil then Result:= nil //for this to work, this cant be a virtual method
else Result:=DoCopyTo;
end;
constructor TBaseRecord.Create(const aId: word; const aData: PArrayOfByte; const aDataSize: integer);
begin
inherited Create;
Id := aId;
Data := aData;
DataSize := aDataSize;
end;
destructor TBaseRecord.Destroy;
begin
if Data<>nil then FreeMem(Data);
FreeAndNil(Continue);
inherited;
end;
function TBaseRecord.DoCopyTo: TBaseRecord;
var
NewData: PArrayOfByte;
begin
GetMem(NewData, DataSize);
try
Move(Data^, NewData^, DataSize);
Result:= ClassOfTBaseRecord(ClassType).Create(Id, NewData, DataSize);
except
FreeMem(NewData);
raise;
end;
if Continue<>nil then Result.Continue:= Continue.CopyTo as TContinueRecord;
end;
procedure TBaseRecord.SaveDataToStream(const Workbook: TStream;
const aData: PArrayOfByte);
begin
if Workbook.Write(Id, Sizeof(Id)) <> Sizeof(Id) then raise Exception.Create(ErrCantWrite);
if Workbook.Write(DataSize, Sizeof(DataSize)) <> Sizeof(DataSize) then raise Exception.Create(ErrCantWrite);
if DataSize > 0 then
if Workbook.Write(aData^, DataSize) <> DataSize then
raise Exception.Create(ErrCantWrite);
end;
procedure TBaseRecord.SaveToStream(const Workbook: TStream; const NeedsRecalc: boolean);
begin
SaveDataToStream(Workbook, Data);
if Continue<>nil then Continue.SaveToStream(Workbook, NeedsRecalc);
end;
function TBaseRecord.TotalSize: integer;
begin
Result:=SizeOf(TRecordHeader)+ DataSize;
if Continue<>nil then Result:=Result+Continue.TotalSize;
end;
function TBaseRecord.TotalSizeNoHeaders: integer;
begin
Result:=DataSize;
if Continue<>nil then Result:=Result+Continue.TotalSizeNoHeaders;
end;
{ TBaseRowColRecord }
procedure TBaseRowColRecord.ArrangeInsertRowsAndCols(const aRowPos, aRowCount, aColPos, aColCount:integer; const SheetInfo: TSheetInfo);
begin
if DataSize<4 then raise Exception.CreateFmt(ErrWrongExcelRecord,[Id]);
if (SheetInfo.InsSheet<0) or (SheetInfo.FormulaSheet<> SheetInfo.InsSheet) then exit;
if aRowPos<= Row then IncWord(Data, 0, aRowCount, Max_Rows); //row;
//Issues if we have 256 columns.
if aColPos<= Column then IncWord(Data, 2, aColCount, Max_Columns); //col;
end;
constructor TBaseRowColRecord.Create(const aId: word; const aData: PArrayOfByte; const aDataSize: integer);
begin
inherited;
if DataSize<4 then raise Exception.CreateFmt(ErrWrongExcelRecord,[Id]);
end;
procedure TBaseRowColRecord.ArrangeCopyRowsAndCols(const RowOffset, ColOffset: integer);
begin
if DataSize<4 then raise Exception.CreateFmt(ErrWrongExcelRecord,[Id]);
SetWord(Data, 0, Row+RowOffset); //row;
SetWord(Data, 2, Column+ColOffset); //col;
end;
function TBaseRowColRecord.GetColumn: word;
begin
GetColumn:=GetWord(Data,2);
end;
function TBaseRowColRecord.GetRow: word;
begin
GetRow:=GetWord(Data,0);
end;
procedure TBaseRowColRecord.SetColumn(Value: word);
begin
SetWord(Data,2,Value);
end;
procedure TBaseRowColRecord.SetRow(Value: word);
begin
SetWord(Data,0,Value);
end;
{ TIgnoreRecord }
procedure TIgnoreRecord.SaveToStream(const Workbook: TStream; const NeedsRecalc: boolean);
begin
//nothing
end;
function TIgnoreRecord.TotalSize: integer;
begin
Result:=0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -