lazarus-ccr/applications/biffexplorer/bebiffgrid.pas
2016-10-07 09:20:21 +00:00

6701 lines
242 KiB
ObjectPascal
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

unit beBIFFGrid;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Controls, Grids, fpstypes, fpspreadsheet, beTypes;
type
TBIFFBuffer = array of byte;
TBIFFDetailsEvent = procedure(Sender: TObject; ADetails: TStrings) of object;
TRichTextFormattingRun = packed record
FirstIndex, FontIndex: Word;
end;
TRichTextFormattingRuns = array of TRichTextFormattingRun;
TBIFFGrid = class(TStringGrid)
private
FRecType: Word;
FBuffer: TBIFFBuffer;
FBufferIndex: LongWord;
FFormat: TsSpreadsheetFormat;
FInfo: Integer;
FTotalSST: Integer;
FCounterSST: Integer;
FPendingCharCount: Integer;
FCurrRow: Integer;
FDetails: TStrings;
FOnDetails: TBIFFDetailsEvent;
function GetStringType: String;
procedure ShowBackup;
procedure ShowBlankCell;
procedure ShowBOF;
procedure ShowBookBool;
procedure ShowBoolCell;
procedure ShowBottomMargin;
procedure ShowCalcCount;
procedure ShowCalcMode;
procedure ShowCellAddress;
procedure ShowCellAddressRange(AFormat: TsSpreadsheetFormat);
procedure ShowClrtClient;
procedure ShowCodePage;
procedure ShowColInfo;
procedure ShowColWidth;
procedure ShowContinue;
procedure ShowCountry;
procedure ShowDateMode;
procedure ShowDBCell;
procedure ShowDefColWidth;
procedure ShowDefinedName;
procedure ShowDefRowHeight;
procedure ShowDelta;
procedure ShowDimensions;
procedure ShowDSF;
procedure ShowEOF;
procedure ShowExcel9File;
procedure ShowExternBook;
procedure ShowExternCount;
procedure ShowExternSheet;
procedure ShowFileSharing;
procedure ShowFnGroupCount;
procedure ShowFont;
procedure ShowFontColor;
procedure ShowFooter;
procedure ShowFormat;
procedure ShowFormatCount;
procedure ShowFormula;
procedure ShowFormulaTokens(ATokenBytes: Integer);
procedure ShowGCW;
procedure ShowHeader;
procedure ShowHideObj;
procedure ShowHyperLink;
procedure ShowHyperLinkTooltip;
procedure ShowInteger;
procedure ShowInterfaceEnd;
procedure ShowInterfaceHdr;
procedure ShowIteration;
procedure ShowIXFE;
procedure ShowLabelCell;
procedure ShowLabelSSTCell;
procedure ShowLeftMargin;
procedure ShowMergedCells;
procedure ShowMMS;
procedure ShowMSODrawing;
procedure ShowMulBlank;
procedure ShowMulRK;
procedure ShowNote;
procedure ShowNumberCell;
procedure ShowObj;
procedure ShowPageSetup;
procedure ShowPalette;
procedure ShowPane;
procedure ShowPassword;
procedure ShowPrecision;
procedure ShowPrintGridLines;
procedure ShowPrintHeaders;
procedure ShowProt4Rev;
procedure ShowProt4RevPass;
procedure ShowProtect;
procedure ShowRecalc;
procedure ShowRefMode;
procedure ShowRefreshAll;
procedure ShowRightMargin;
procedure ShowRK;
procedure ShowRString;
procedure ShowRow;
procedure ShowSelection;
procedure ShowSharedFormula;
procedure ShowSheet;
procedure ShowSheetPR;
procedure ShowSST;
procedure ShowStandardWidth;
procedure ShowString;
procedure ShowStyle;
procedure ShowStyleExt;
procedure ShowTabID;
procedure ShowTopMargin;
procedure ShowTXO;
procedure ShowWindow1;
procedure ShowWindow2;
procedure ShowWindowProtect;
procedure ShowWriteAccess;
procedure ShowWriteProt;
procedure ShowXF;
procedure ShowXFCRC;
procedure ShowXFEXT;
protected
procedure Click; override;
procedure DoExtractDetails;
function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
procedure ExtractString(ABufIndex: Integer; AUnicode: Boolean;
ACharCount: Integer; out AString: String; out ANumbytes: Integer); overload;
procedure ExtractString(ABufIndex: Integer; ALenBytes: Byte; AUnicode: Boolean;
out AString: String; out ANumBytes: Integer;
out ARichTextRuns: TRichTextFormattingRuns;
out ABufIndexOfFirstRichTextRun: LongWord;
IgnoreCompressedFlag: Boolean = false); overload;
procedure ExtractString(ABufIndex: Integer; ALenbytes: Byte; AUnicode: Boolean;
out AString: String; out ANumBytes: Integer;
IgnoreCompressedFlag: Boolean=False); overload;
procedure PopulateGrid;
procedure ShowInRow(var ARow: Integer; var AOffs: LongWord; ASize: Word;
AValue,ADescr: String; ADescrOnly: Boolean = false);
procedure ShowRowColData(var ABufIndex: LongWord);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure SetBIFFNodeData(AData: PBIFFNodeData; ABuffer: TBIFFBuffer;
AFormat: TsSpreadsheetFormat);
published
property TabOrder;
property OnDetails: TBIFFDetailsEvent read FOnDetails write FOnDetails;
property OnSelection;
end;
implementation
uses
StrUtils, Math, lazutf8, lconvencoding,
fpsutils,
beBIFFUtils;
const
ABS_REL: array[boolean] of string = ('abs', 'rel');
constructor TBIFFGrid.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ColCount := 4;
FixedCols := 0;
RowCount := 2;
Cells[0, 0] := 'Offset';
Cells[1, 0] := 'Size';
Cells[2, 0] := 'Value';
Cells[3, 0] := 'Description';
ColWidths[0] := 60;
ColWidths[1] := 60;
ColWidths[2] := 120;
ColWidths[3] := 350;
Options := Options
+ [goThumbTracking, goColSizing, goTruncCellHints, goCellHints]
- [goVertLine, goSmoothScroll];
MouseWheelOption := mwGrid;
FDetails := TStringList.Create;
FPendingCharCount := -1;
end;
destructor TBIFFGrid.Destroy;
begin
FDetails.Free;
inherited;
end;
procedure TBIFFGrid.Click;
begin
inherited;
if (FBuffer <> nil) then
DoExtractDetails;
end;
procedure TBIFFGrid.DoExtractDetails;
begin
if Assigned(FOnDetails) then begin
PopulateGrid;
FOnDetails(self, FDetails);
end;
end;
function TBIFFGrid.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint
): Boolean;
begin
Result := inherited;
Click;
end;
function TBIFFGrid.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint
): Boolean;
begin
Result := inherited;
Click;
end;
{ Reads a string character array starting at ABufIndex. The string is supposed
to have ACharCount character, but less characters are read if the string
extends across the max size of a record and is continued in the next CONTINUE
record.
The string is assumed to be a UTF16 string if AUnicode=true, otherwise it is
an ansi string. }
procedure TBIFFGrid.ExtractString(ABufIndex: Integer; AUnicode: Boolean;
ACharCount: Integer; out AString: String; out ANumbytes: Integer);
var
sa: AnsiString;
sw: WideString;
n: Integer;
begin
if AUnicode then // uncompressed unicode --> 2 bytes per char
begin
if ABufIndex + ACharCount * SizeOf(WideChar) >= Length(FBuffer) then
begin
n := (Length(FBuffer) - ABufIndex) div SizeOf(WideChar);
FPendingCharCount := ACharCount - n; // number of chars to be read from subsequent CONTINUE record
end else
begin
n := ACharCount;
FPendingCharCount := 0;
end;
SetLength(sw, n);
ANumBytes := n * SizeOf(WideChar);
Move(FBuffer[ABufIndex], sw[1], ANumBytes);
AString := UTF8Encode(WideStringLEToN(sw));
end else
begin // ansi or compressed unicode
if ABufIndex + ACharCount >= Length(FBuffer) then
begin
n := Length(FBuffer) - ABufIndex;
FPendingCharCount := ACharCount - n; // number of chars in subsequent CONTINUE record
end else
begin
n := ACharCount;
FPendingCharCount := 0;
end;
SetLength(sa, n);
ANumBytes := n;
Move(FBuffer[ABufIndex], sa[1], ANumBytes);
AString := CP1252ToUTF8(sa); // to do: use code page of file
end;
end;
procedure TBIFFGrid.ExtractString(ABufIndex: Integer; ALenBytes: Byte; AUnicode: Boolean;
out AString: String; out ANumBytes: Integer;
IgnoreCompressedFlag: Boolean = false);
var
rtfRuns: TRichTextFormattingRuns;
rtfIndex: LongWord;
begin
ExtractString(ABufIndex, ALenbytes, AUnicode, AString, ANumBytes,
rtfRuns, rtfIndex, IgnoreCompressedFlag);
end;
procedure TBIFFGrid.ExtractString(ABufIndex: Integer; ALenBytes: Byte; AUnicode: Boolean;
out AString: String; out ANumBytes: Integer;
out ARichTextRuns: TRichTextFormattingRuns;
out ABufIndexOfFirstRichTextRun: LongWord;
IgnoreCompressedFlag: Boolean = false);
var
ls: Integer; // Character count of string
w: Word;
dw: DWord;
optn: Byte;
n: Integer; // Byte count in string character array
asianPhoneticBytes: DWord;
numRichRuns: Word;
offs: Integer;
rtfBufIndex: Int64;
rtfIndex: Integer;
begin
ABufIndexOfFirstRichTextRun := LongWord(-1);
SetLength(ARichTextRuns, 0);
if Length(FBuffer) = 0 then begin
AString := '';
ANumBytes := 0;
exit;
end;
if ALenBytes = 1 then
ls := FBuffer[ABufIndex]
else begin
Move(FBuffer[ABufIndex], w, 2);
ls := WordLEToN(w);
end;
if AUnicode then begin
offs := ALenBytes;
optn := FBuffer[ABufIndex + ALenBytes];
inc(offs, 1);
if optn and $08 <> 0 then // rich text
begin
Move(FBuffer[ABufIndex + offs], w, 2);
numRichRuns := WordLEToN(w);
inc(offs, 2);
end else
numRichRuns := 0;
SetLength(ARichTextRuns, numRichRuns);
if optn and $04 <> 0 then // Asian phonetic
begin
Move(FBuffer[ABufIndex + offs], dw, 4);
AsianPhoneticBytes := DWordLEToN(dw);
inc(offs, 4);
end else
asianPhoneticBytes := 0;
if (optn and $01 = 0) and (not IgnoreCompressedFlag) then
// compressed --> 1 byte per character
ExtractString(ABufIndex + offs, false, ls, AString, n)
else
// non-compressed unicode
ExtractString(ABufIndex + offs, true, ls, AString, n);
ANumBytes := offs + n + numRichRuns * 4 + asianPhoneticBytes;
rtfIndex := 0;
rtfBufIndex := ABufIndex + offs + n;
ABufIndexOfFirstRichTextRun := rtfBufIndex;
while rtfIndex < numRichRuns do begin
Move(FBuffer[rtfBufIndex], w, 2);
ARichTextRuns[rtfIndex].FirstIndex := WordLEToN(w);
Move(FBuffer[rtfBufIndex+2], w, 2);
ARichTextRuns[rtfIndex].FontIndex := WordLEToN(w);
inc(rtfIndex);
inc(rtfBufIndex, 4);
end;
end else
begin
// ansi string
SetLength(ARichTextRuns, 0); // no rich text formatting for ansi strings
ExtractString(ABufIndex + ALenBytes, false, ls, AString, n);
ANumbytes := ALenBytes + n;
end;
end;
function TBIFFGrid.GetStringType: String;
begin
case FFormat of
sfExcel2: Result := 'Byte';
sfExcel5: Result := 'Byte';
sfExcel8: Result := 'Unicode';
end;
end;
procedure TBIFFGrid.PopulateGrid;
begin
FBufferIndex := 0;
FCurrRow := FixedRows;
FDetails.Clear;
case FRecType of
$0000, $0200:
ShowDimensions;
$0001, $0201:
ShowBlankCell;
$0002:
ShowInteger;
$0003, $0203:
ShowNumberCell;
$0004, $0204:
ShowLabelCell;
$0005, $0205:
ShowBoolCell;
$0006:
ShowFormula;
$0007, $0207:
ShowString;
$0008, $0208:
ShowRow;
$0009, $0209, $0409, $0809:
ShowBOF;
$000A:
ShowEOF;
$000C:
ShowCalcCount;
$000D:
ShowCalcMode;
$000E:
ShowPrecision;
$000F:
ShowRefMode;
$0010:
ShowDelta;
$0011:
ShowIteration;
$0012:
ShowProtect;
$0013:
ShowPassword;
$0014:
ShowHeader;
$0015:
ShowFooter;
$0016:
ShowExternCount;
$0017:
ShowExternSheet;
$0018, $0218:
ShowDefinedName;
$0019:
ShowWindowProtect;
$001C:
ShowNote;
$001D:
ShowSelection;
$001E, $041E:
ShowFormat;
$001F:
ShowFormatCount;
$0022:
ShowDateMode;
$0024:
ShowColWidth;
$0025, $0225:
ShowDefRowHeight;
$0026:
ShowLeftMargin;
$0027:
ShowRightMargin;
$0028:
ShowTopMargin;
$0029:
ShowBottomMargin;
$002A:
ShowPrintHeaders;
$002B:
ShowPrintGridLines;
$0031:
ShowFont;
$003C:
ShowContinue;
$003D:
ShowWindow1;
$003E, $023E:
ShowWindow2;
$0040:
ShowBackup;
$0041:
ShowPane;
$0042:
ShowCodePage;
$0043:
ShowXF;
$0044:
ShowIXFE;
$0045:
ShowFontColor;
$0055:
ShowDefColWidth;
$005B:
ShowFileSharing;
$005C:
ShowWriteAccess;
$005D:
ShowObj;
$005F:
ShowRecalc;
$007D:
ShowColInfo;
$0081:
ShowSheetPR;
$0085:
ShowSheet;
$0086:
ShowWriteProt;
$008C:
ShowCountry;
$008D:
ShowHideObj;
$0092:
ShowPalette;
$099:
ShowStandardWidth;
$00A1:
ShowPageSetup;
$00AB:
ShowGCW;
$00C1:
ShowMMS;
$009C:
ShowFnGroupCount;
$00BE:
ShowMulBlank;
$00BD:
ShowMulRK;
$00D6:
ShowRString;
$00D7:
ShowDBCell;
$00DA:
ShowBookBool;
$00E0:
ShowXF;
$00E1:
ShowInterfaceHdr;
$00E2:
ShowInterfaceEnd;
$00E5:
ShowMergedCells;
$00EC:
ShowMSODrawing;
$00FC:
ShowSST;
$00FD:
ShowLabelSSTCell;
$013D:
ShowTabID;
$0161:
ShowDSF;
$01AE:
ShowExternBook;
$01AF:
ShowProt4Rev;
$01B6:
ShowTXO;
$01B7:
ShowRefreshAll;
$01B8:
ShowHyperlink;
$01BC:
ShowProt4RevPass;
$01C0:
ShowExcel9File;
$027E:
ShowRK;
$0293:
ShowStyle;
$04BC:
ShowSharedFormula;
$0800:
ShowHyperlinkTooltip;
$087C:
ShowXFCRC;
$087D:
ShowXFEXT;
$0892:
ShowStyleExt;
$105C:
ShowClrtClient;
else
RowCount := 2;
Rows[1].Clear;
end;
end;
procedure TBIFFGrid.SetBIFFNodeData(AData: PBIFFNodeData;
ABuffer: TBIFFBuffer; AFormat: TsSpreadsheetFormat);
begin
if AData = nil then
exit;
FFormat := AFormat;
FRecType := AData^.RecordID;
FInfo := AData^.Tag;
SetLength(FBuffer, Length(ABuffer));
if Length(FBuffer) > 0 then
Move(ABuffer[0], FBuffer[0], Length(FBuffer));
PopulateGrid;
if Assigned(FOnDetails) then FOnDetails(self, FDetails);
end;
procedure TBIFFGrid.ShowBackup;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Save backup copy of workbook:'#13);
if w = 0
then FDetails.Add('0 = no backup')
else FDetails.Add('1 = backup copy is saved when workbook is saved');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
'Save backup copy of workbook');
end;
procedure TBIFFGrid.ShowBlankCell;
var
numBytes: Integer;
b: Byte = 0;
w: Word = 0;
dbl: Double;
begin
RowCount := IfThen(FFormat = sfExcel2, FixedRows + 5, FixedRows + 3);
// Offset 0: Row & Offset 2: Column
ShowRowColData(FBufferIndex);
// Offset 4: Cell attributes (BIFF2) or XF record index (> BIFF2)
if FFormat = sfExcel2 then begin
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
if Row = FCurrRow then begin
FDetails.Add('Cell protection and XF index:'#13);
FDetails.Add(Format('Bits 5-0 = %d: XF Index', [b and $3F]));
case b and $40 of
0: FDetails.Add('Bit 6 = 0: Cell is NOT locked.');
1: FDetails.Add('Bit 6 = 1: Cell is locked.');
end;
case b and $80 of
0: FDetails.Add('Bit 7 = 0: Formula is NOT hidden.');
1: FDetails.Add('Bit 7 = 1: Formula is hidden.');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b, b]),
'Cell protection and XF index');
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
if Row = FCurrRow then begin
FDetails.Add('Indexes to format and font records:'#13);
FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f]));
FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6]));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b, b]),
'Indexes of format and font records');
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
if Row = FCurrRow then begin
FDetails.Add('Cell style:'#13);
case b and $07 of
0: FDetails.Add('Bits 2-0 = 0: Horizontal alignment is GENERAL');
1: FDetails.Add('Bits 2-0 = 1: Horizontal alignment is LEFT');
2: FDetails.Add('Bits 2-0 = 2: Horizontal alignment is CENTERED');
3: FDetails.Add('Bits 2-0 = 3: Horizontal alignment is RIGHT');
4: FDetails.Add('Bits 2-0 = 4: Horizontal alignment is FILLED');
end;
if b and $08 = 0
then FDetails.Add('Bit 3 = 0: Cell has NO left border')
else FDetails.Add('Bit 3 = 1: Cell has left black border');
if b and $10 = 0
then FDetails.Add('Bit 4 = 0: Cell has NO right border')
else FDetails.Add('Bit 4 = 1: Cell has right black border');
if b and $20 = 0
then FDetails.Add('Bit 5 = 0: Cell has NO top border')
else FDetails.Add('Bit 5 = 1: Cell has top black border');
if b and $40 = 0
then FDetails.Add('Bit 6 = 0: Cell has NO bottom border')
else FDetails.Add('Bit 6 = 1: Cell has bottom black border');
if b and $80 = 0
then FDetails.Add('Bit 7 = 0: Cell has NO shaded background')
else FDetails.Add('Bit 7 = 1: Cell has shaded background');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]),
'Cell style');
end else
begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrROw, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
'Index of XF record');
end;
end;
procedure TBIFFGrid.ShowBOF;
var
numBytes: Integer;
w: Word;
s: String;
begin
case FFormat of
sfExcel2: RowCount := FixedRows + 2;
{ //Excel3 & 4 not supported by fpspreadsheet
sfExcel3, sfExcel4: RowCount := FixedRows + 3;
}
sfExcel5: RowCount := FixedRows + 4;
sfExcel8: RowCount := FixedRows + 6;
end;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('BIFF version:'#13);
case FRecType of
$0009,
$0209,
$0409: FDetails.Add('not used');
$0809: case FFormat of
sfExcel5: FDetails.Add('$0500 = BIFF5');
sfExcel8: FDetails.Add('$0600 = BIFF8');
end;
else case w of
$0000: FDetails.Add('$0000 = BIFF5');
$0200: FDetails.Add('$0200 = BIFF2');
$0300: FDetails.Add('$0300 = BIFF3');
$0400: FDetails.Add('$0400 = BIFF4');
$0500: FDetails.Add('$0500 = BIFF5');
$0600: FDetails.Add('$0600 = BIFF8');
end;
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
'BIFF version');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
s := '$0010=Sheet, $0020=Chart, $0040=Macro sheet';
if FFormat > sfExcel2 then
s := '$0005=WB globals, $0006=VB module, ' + s + ', $0100=Workspace';
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Type of data:'#13);
FDetails.Add(Format('$%.4x = %s', [w, BofName(w)]));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
Format('Type of data (%s)', [s]));
if FFormat > sfExcel2 then begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
{ Excel3/4 not supported in fpSpreadsheet
if FFormat in [sfExcel3, sfExcel4] then
ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(WordLEToN(w)),
'not used')
else}
begin
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Build identifier (must not be zero)');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Build year (must not be zero)');
end;
end;
if FFormat = sfExcel8 then begin
numBytes := 4;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'File history flags');
numBytes :=4;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Lowest Excel version that can read all records of this file');
end;
end;
procedure TBIFFGrid.ShowBookBool;
var
numbytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Some properties assosciated with notebook:'#13);
if w and $0001 = 0
then FDetails.Add('Bit 0 = 0: External link values are saved.')
else FDetails.Add('Bit 0 = 1: External link values are NOT saved.');
FDetails.Add('Bit 1: to be ignored');
if w and $0004 = 0
then FDetails.Add('Bit 2 = 0: Workbook does not have a mail envelope')
else FDetails.Add('Bit 2 = 1: Workbook has a mail envelope');
if w and $0008 = 0
then FDetails.Add('Bit 3 = 0: Mail envelope is NOT visible.')
else FDetails.Add('Bit 3 = 1: Mail envelope is visible.');
if w and $0010 = 0
then FDetails.Add('Bit 4 = 0: Mail envelope has NOT been initialized.')
else FDetails.Add('Bit 4 = 1: Mail envelope has been initialized.');
case (w and $0060) shr 5 of
0: FDetails.Add('Bits 5-6 (Update external links) = 0: Prompt user to update');
1: FDetails.Add('Bits 5-6 (Update external linls) = 1: Do not update, and do not prompt user.');
2: FDetails.Add('Bits 5-6 (Update external links) = 2: Silently update external links.');
end;
FDetails.Add('Bit 7: undefined, must be ignored');
if w and $0100 = 0
then FDetails.Add('Bit 8 = 0: Do not hide borders of tables that do not contain the active cell')
else FDetails.Add('Bit 8 = 1: Hide borders of tables that do not contain the active cell');
FDetails.Add('Bits 9-15: MUST BE zero, MUST be ignored');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]),
'Specifies some properties assosciated with a workbook');
end;
procedure TBIFFGrid.ShowBoolCell;
var
numBytes: Integer;
w: Word;
b: Byte;
begin
if FFormat = sfExcel2 then
RowCount := FixedRows + 7
else
RowCount := FixedRows + 5;
ShowRowColData(FBufferIndex);
if FFormat = sfExcel2 then begin
numBytes := 1;
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Cell protection and XF index:'#13);
FDetails.Add(Format('Bits 5-0 = %d: XF Index', [b and $3F]));
case b and $40 of
0: FDetails.Add('Bit 6 = 0: Cell is NOT locked.');
1: FDetails.Add('Bit 6 = 1: Cell is locked.');
end;
case b and $80 of
0: FDetails.Add('Bit 7 = 0: Formula is NOT hidden.');
1: FDetails.Add('Bit 7 = 1: Formula is hidden.');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
'Cell protection and XF index');
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Indexes to format and font records:'#13);
FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f]));
FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6]));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
'Indexes of format and font records');
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Cell style:'#13);
case b and $07 of
0: FDetails.Add('Bits 2-0 = 0: Horizontal alignment is GENERAL');
1: FDetails.Add('Bits 2-0 = 1: Horizontal alignment is LEFT');
2: FDetails.Add('Bits 2-0 = 2: Horizontal alignment is CENTERED');
3: FDetails.Add('Bits 2-0 = 3: Horizontal alignment is RIGHT');
4: FDetails.Add('Bits 2-0 = 4: Horizontal alignment is FILLED');
end;
if b and $08 = 0
then FDetails.Add('Bit 3 = 0: Cell has NO left border')
else FDetails.Add('Bit 3 = 1: Cell has left black border');
if b and $10 = 0
then FDetails.Add('Bit 4 = 0: Cell has NO right border')
else FDetails.Add('Bit 4 = 1: Cell has right black border');
if b and $20 = 0
then FDetails.Add('Bit 5 = 0: Cell has NO top border')
else FDetails.Add('Bit 5 = 1: Cell has top black border');
if b and $40 = 0
then FDetails.Add('Bit 6 = 0: Cell has NO bottom border')
else FDetails.Add('Bit 6 = 1: Cell has bottom black border');
if b and $80 = 0
then FDetails.Add('Bit 7 = 0: Cell has NO shaded background')
else FDetails.Add('Bit 7 = 1: Cell has shaded background');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]),
'Cell style');
end else
begin // BIFF3 - BIFF 8
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrROw, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
'Index of XF record');
end;
// boolean value
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numbytes,
Format('%d (%s)', [b, Uppercase(BoolToStr(Boolean(b), true))]),
'Boolean value (0=FALSE, 1=TRUE)'
);
// bool/error flag
numBytes := 1;
b := FBuffer[FBufferIndex];
if b = 0 then
ShowInRow(FCurrRow, FBufferIndex, numbytes, '0 (boolean value)',
'Boolean/Error value flag (0=boolean, 1=error value)')
else
ShowInRow(FCurrRow, FBufferIndex, numbytes, '1 (error value)',
'Boolean/Error value flag (0=boolean, 1=error value)');
end;
procedure TBIFFGrid.ShowBottomMargin;
var
numBytes: Integer;
dbl: Double;
begin
RowCount := FixedRows + 1;
numBytes := 8;
Move(FBuffer[FBufferIndex], dbl, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl),
'Bottom page margin in inches (IEEE 754 floating-point value, 64-bit double precision)');
end;
procedure TBIFFGrid.ShowCalcCount;
var
numBytes: Word;
w: Word;
begin
RowCount := FixedRows + 1;
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'Maximum number of iterations allowed in circular references');
end;
procedure TBIFFGrid.ShowCalcMode;
var
numBytes: Word;
w: word;
s: String;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if w = $FFFF then
s := '1 = automatically except for multiple table operations'
else if w = 0 then
s := '0 = manually'
else if w = 1 then
s := '1 = automatically (default)';
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), s);
end;
procedure TBIFFGrid.ShowCellAddress;
{ Note: The bitmask assignment to relative column/row is reversed in relation
to OpenOffice documentation in order to match with Excel files. }
var
numBytes: Word;
b: Byte;
w: Word;
r,c: Integer;
s: String;
begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes); // row --> w
r := WordLEToN(w);
if FFormat = sfExcel8 then begin
numBytes := 2;
Move(FBuffer[FBufferIndex+2], w, numBytes); // column --w1
c := WordLEToN(w);
if FCurrRow = Row then begin
FDetails.Add('RowIndex information:'#13);
FDetails.Add(Format('RowIndex = %d (%s)', [r, ABS_REL[c and $4000 <> 0]]));
end;
s := Format('%d ($%.4x)', [r, r]);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Row index');
if FCurrRow = Row then begin
FDetails.Add('ColIndex information:'#13);
FDetails.Add(Format('Bits 0-13: ColIndex = %d (%s)', [c and $3FFF, ABS_REL[c and $8000 <> 0]]));
if c and $4000 = 0
then FDetails.Add('Bit 14=0: absolute column index')
else FDetails.Add('Bit 14=1: relative column index');
if c and $8000 = 0
then FDetails.Add('Bit 15=0: absolute row index')
else FDetails.Add('Bit 15=1: relative row index');
end;
s := Format('%d ($%.4x)', [c, c]);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Column index');
end else
begin
numbytes := 1;
Move(FBuffer[FBufferIndex+2], b, numBytes);
c := b;
if FCurrRow = Row then begin
FDetails.Add('RowIndex information:'#13);
FDetails.Add(Format('Bits 0-13: RowIndex = %d (%s)', [r and $3FFF, ABS_REL[r and $4000 <> 0]]));
if r and $4000 = 0
then FDetails.Add('Bit 14=0: absolute column index')
else FDetails.Add('Bit 14=1: relative column index');
if r and $8000 = 0
then FDetails.Add('Bit 15=0: absolute row index')
else FDetails.Add('Bit 15=1: relative row index');
end;
//s := Format('$%.4x (%d, %s)', [r, r and $3FFF, ABS_REL[r and $4000 <> 0]]);
s := Format('%d ($%.4x)', [r, r]);
ShowInRow(FCurrRow, FBufferIndex, 2, s, 'Row index');
if FCurrRow = Row then begin
FDetails.Add('ColIndex information:'#13);
FDetails.Add(Format('ColIndex = %d (%s)', [c, ABS_REL[r and $8000 <> 0]]));
end;
//s := Format('$%.2x (%d, %s)', [c, c, ABS_REL[r and $8000 <> 0]]);
s := Format('%d ($%.4x)', [c, c]);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Column index');
end;
end;
procedure TBIFFGrid.ShowCellAddressRange(AFormat: TsSpreadsheetFormat);
{ Note: The bitmask assignment to relative column/row is reversed in relation
to OpenOffice documentation in order to match with Excel files.
The spreadsheet format is passed as a parameter because some BIFF8 records
used these fields in BIFF5 format. }
var
numbytes: Word;
b: Byte;
w: Word;
r, c, r2, c2: Integer;
s: String;
begin
if AFormat = sfExcel8 then begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
r := WordLEToN(w);
if FCurrRow = Row then begin
FDetails.Add('RowIndex information:'#13);
FDetails.Add(Format('RowIndex = %d (%s)', [r, ABS_REL[c and $4000 <> 0]]));
end;
s := Format('%d ($%.4x)', [r, r]);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'First row index');
Move(FBuffer[FBufferIndex], w, numBytes);
r2 := WordLEToN(w);
if FCurrRow = Row then begin
FDetails.Add('RowIndex information:'#13);
FDetails.Add(Format('RowIndex = %d (%s)', [r2, ABS_REL[c and $4000 <> 0]]));
end;
s := Format('%d ($%.4x)', [r2, r2]);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Last row index');
Move(FBuffer[FBufferIndex], w, numBytes); // column
c := WordLEToN(w);
Move(FBuffer[FBufferIndex+2], w, numBytes);
c2 := WordLEToN(w);
if FCurrRow = Row then begin
FDetails.Add('ColIndex information:'#13);
FDetails.Add(Format('Bits 0-13: ColIndex = %d (%s)', [c and $3FFF, ABS_REL[c and $8000 <> 0]]));
if c and $4000 = 0
then FDetails.Add('Bit 14=0: absolute column index')
else FDetails.Add('Bit 14=1: relative column index');
if c and $8000 = 0
then FDetails.Add('Bit 15=0: absolute row index')
else FDetails.Add('Bit 15=1: relative row index');
end;
s := Format('%d ($%.4x)', [c, c]);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'First column index');
if FCurrRow = Row then
begin
FDetails.Add('ColIndex information:'#13);
FDetails.Add(Format('Bits 0-13: ColIndex = %d (%s)', [c2 and $3FFF, ABS_REL[c2 and $8000 <> 0]]));
if c2 and $4000 = 0
then FDetails.Add('Bit 14=0: absolute column index')
else FDetails.Add('Bit 14=1: relative column index');
if c2 and $8000 = 0
then FDetails.Add('Bit 15=0: absolute row index')
else FDetails.Add('Bit 15=1: relative row index');
end;
s := Format('%d ($%.4x)', [c2, c2]);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Last column index');
end
else
begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
r := WordLEToN(w);
Move(FBuffer[FBufferIndex+2], w, numBytes);
r2 := WordLEToN(w);
numbytes := 1;
c := FBuffer[FBufferIndex+4];
c2 := FBuffer[FBufferIndex+5];
if FCurrRow = Row then
begin
FDetails.Add('RowIndex information:'#13);
FDetails.Add(Format('Bits 0-13: RowIndex = %d (%s)', [r and $3FFF, ABS_REL[r and $4000 <> 0]]));
if r and $4000 = 0
then FDetails.Add('Bit 14=0: absolute column index')
else FDetails.Add('Bit 14=1: relative column index');
if r and $8000 = 0
then FDetails.Add('Bit 15=0: absolute row index')
else FDetails.Add('Bit 15=1: relative row index');
end;
s := Format('%d ($%.4x)', [r, r]);
ShowInRow(FCurrRow, FBufferIndex, 2, s, 'First row index');
if FCurrRow = Row then
begin
FDetails.Add('RowIndex information:'#13);
FDetails.Add(Format('Bits 0-13: RowIndex = %d (%s)', [r2 and $3FFF, ABS_REL[r2 and $4000 <> 0]]));
if r2 and $4000 = 0
then FDetails.Add('Bit 14=0: absolute column index')
else FDetails.Add('Bit 14=1: relative column index');
if r2 and $8000 = 0
then FDetails.Add('Bit 15=0: absolute row index')
else FDetails.Add('Bit 15=1: relative row index');
end;
s := Format('%d ($%.4x)', [r2, r2]);
ShowInRow(FCurrRow, FBufferIndex, 2, s, 'Last row index');
if FCurrRow = Row then
begin
FDetails.Add('ColIndex information:'#13);
FDetails.Add(Format('ColIndex = %d (%s)', [c, ABS_REL[r and $8000 <> 0]]));
end;
s := Format('%d ($%.4x)', [c, c]);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'First column index');
if FCurrRow = Row then
begin
FDetails.Add('ColIndex information:'#13);
FDetails.Add(Format('ColIndex = %d (%s)', [c2, ABS_REL[r2 and $8000 <> 0]]));
end;
s := Format('%d ($%.4x)', [c2, c2]);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Last column index');
end;
end;
procedure TBIFFGrid.ShowClrtClient;
var
w: Word;
dw: DWord;
numbytes: Word;
begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
RowCount := FixedRows + w + 1;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Number of colors (must be 3)');
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
dw := DWordLEToN(dw);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]),
'Foreground color (system window text color)');
Move(FBuffer[FBufferIndex], dw, numbytes);
dw := DWordLEToN(dw);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]),
'Background color (system window color)');
Move(FBuffer[FBufferIndex], dw, numbytes);
dw := DWordLEToN(dw);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]), '???');
end;
procedure TBIFFGrid.ShowCodePage;
var
numBytes: Word;
w: Word;
s: String;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
s := CodePageName(w);
if Row = FCurrRow then begin
FDetails.Add('Code page:'#13);
FDetails.Add(Format('$%.04x = %s', [w, s]));
end;
if s <> '' then s := 'Code page identifier (' + s + ')' else s := 'Code page identifier';
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), s);
end;
procedure TBIFFGrid.ShowColInfo;
var
numBytes: Integer;
w: Word;
begin
if FFormat = sfExcel2 then
exit;
RowCount := FixedRows + 5;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index of first column in range');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index of last column in range');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d (%f characters)', [w, w/256]),
'Width of the columns in 1/256 of the width of the zero character, using default font (first FONT record in the file)');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index to XF record for default column formatting');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Column options:'#13);
if w and $0001 = 0
then FDetails.Add('Bit $0001 = 0: Columns are NOT hidden')
else FDetails.Add('Bit $0001 = 1: Columns are hidden');
FDetails.Add(Format('Bits $0700 = %d: Outline level of the columns (0 = no outline)', [(w and $0700) shr 8]));
if w and $1000 = 0
then FDetails.Add('Bit $1000 = 0: Columns are NOT collapsed')
else FDetails.Add('Bit $1000 = 1: Columns are collapsed');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), 'Option flags');
end;
procedure TBIFFGrid.ShowColWidth;
var
numBytes: Integer;
w: Word;
b: Byte;
begin
if FFormat <> sfExcel2 then
exit;
RowCount := FixedRows + 3;
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
'Index of first column');
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
'Index of last column');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Width of the columns in 1/256 of the width of the zero character, using default font (first FONT record in the file)');
end;
procedure TBIFFGrid.ShowContinue;
var
numbytes: Integer;
s: String;
sa: ansistring;
sw: widestring;
ls: Integer;
i, j: Integer;
w: Word;
n: Integer;
run: Integer;
total2: Integer;
optn: Byte;
rtfRuns: TRichTextFormattingRuns;
rtfBufferIndex: LongWord;
begin
case FInfo of
BIFFNODE_TXO_CONTINUE1:
begin
RowCount := FixedRows + 1;
numbytes := Length(FBuffer);
if FBuffer[FBufferIndex] = 0 then begin
ls := Length(FBuffer)-1;
SetLength(sa, ls);
Move(FBuffer[FBufferIndex+1], sa[1], ls);
s := CP1252ToUTF8(sa);
end else
if FBuffer[FBufferIndex] = 1 then begin
ls := (Length(FBuffer) - 1) div SizeOf(WideChar);
SetLength(sw, ls);
Move(FBuffer[FBufferIndex+1], sw[1], ls*SizeOf(WideChar));
s := UTF8Encode(sw);
end else
s := 'ERROR!!!';
s := UTF8StringReplace(s, #$0A, '[/n]', [rfReplaceAll]);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Comment text');
end;
BIFFNODE_TXO_CONTINUE2:
begin
RowCount := FixedRows + 1000;
n := 0;
numBytes := 2;
run := 1;
while FBufferIndex < Length(FBuffer) - 4*SizeOf(Word) do begin
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), Format(
'Run %d: Index of first character using this font (0-based)', [run]));
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToSTr(WordLEToN(w)),
Format('Run %d: Index to FONT record', [run]));
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInrow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Not used');
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInrow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Not used');
inc(n);
inc(run);
end;
// lastRun
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInrow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Number of characters');
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInrow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Not used');
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInrow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Not used');
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Not used');
inc(n);
RowCount := FixedRows + n;
end;
BIFFNODE_SST_CONTINUE:
begin // Continues an SST record
if FPendingCharCount = -1 then
begin
RowCount := FixedRows + 1;
ShowInRow(FCurrRow, FBufferIndex, 0, '', 'Please select preceding SST record first.');
exit;
end;
RowCount := FixedRows + FTotalSST;
n := 0;
optn := FBuffer[FBufferIndex];
if optn and $01 = $01 then // wide characters
ExtractString(FBufferIndex+1, true, FPendingCharCount, s, numBytes)
else
ExtractString(FBufferIndex+1, false, FPendingCharCount, s, numbytes);
FPendingCharCount := -1;
inc(numbytes, 1);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s, Format('Shared String #%d (rest)', [FCounterSST]));
inc(n);
FPendingCharCount := -1;
for i:=FCounterSST+1 to FTotalSST do
begin
FCounterSST := i;
ExtractString(FBufferIndex, 2, true, s, numBytes, rtfRuns, rtfBufferIndex);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, Format('Shared string #%d', [i]));
inc(n);
if Length(rtfRuns) > 0 then begin
numBytes := 2;
for j:=0 to High(rtfRuns) do
begin
ShowInRow(FCurrRow, rtfBufferIndex, 2, IntToStr(rtfRuns[j].FirstIndex),
Format('Rich-Text formatting run #%d, index of first character', [j]));
ShowInRow(FCurrRow, rtfBufferIndex, 2, IntToStr(rtfRuns[j].FontIndex),
Format('Rich-Text formatting run #%d, font index', [j]));
inc(n, 2);
end;
end;
if FPendingCharCount > 0 then
begin
FInfo := BIFFNODE_SST_CONTINUE;
break;
end;
end;
RowCount := FixedRows + n;
if FPendingCharCount = 0 then
FPendingCharCount := -1;
end;
end;
end;
procedure TBIFFGrid.ShowCountry;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + 2;
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Windows country identifier for UI language of Excel');
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Windows country identifier of system regional settings');
end;
procedure TBIFFGrid.ShowDateMode;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'0 = Base date is 1899-Dec-31, 1 = Base date is 1904-Jan-01');
end;
procedure TBIFFGrid.ShowDBCell;
var
i, n: Integer;
dw: DWord;
w: Word;
numBytes: Integer;
begin
if FFormat < sfExcel5 then exit;
n := (Length(FBuffer) - 4) div 2;
RowCount := FixedRows + 1 + n;
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)),
'Relative offset to first ROW record in the Row Block');
numBytes := 2;
for i:=1 to n do begin
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Relative offsets to calculate stream position of the first cell record in row');
end;
end;
procedure TBIFFGrid.ShowDefColWidth;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Column width in characters, using the width of the zero character from default '+
'font (first FONT record in the file) + some extra space.');
end;
procedure TBIFFGrid.ShowDefinedName;
var
numBytes: Integer;
b: Byte;
w: Word;
isFuncMacro: Boolean;
lenName: Word;
ansistr: AnsiString;
widestr: WideString;
s: String;
macro: Boolean;
formulaSize: Word;
firstTokenBufIdx: Integer;
token: Byte;
r,c, r2,c2: Integer;
builtinName: Boolean;
begin
BeginUpdate;
RowCount := FixedRows + 1000;
// Brute force simplification because of unknown row count at this point
// Will be reduced at the end.
builtinName := false;
if FFormat = sfExcel2 then
begin
numBytes := 1;
b := FBuffer[FBufferIndex];
isFuncMacro := b and $02 <> 0;
if Row = FCurrRow then begin
FDetails.Add('Option flags:'#13);
if b and $02 = 0 then
FDetails.Add(' Bit $02 = 0: NO function macro or command macro')
else
FDetails.Add('* Bit $02 = 1: Function macro or command macro');
if b and $04 = 0 then
FDetails.Add(' Bit $04 = 0: NO Complex function (array formula or user defined)')
else
FDetails.Add('* Bit $04 = 1: Complex function (array formula or user defined)');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]),
'Option flags');
numBytes := 1;
b := FBuffer[FBufferIndex];
if isFuncMacro then
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
'$01 = Function macro, $02 = Command macro')
else
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
'unknown');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
'Keyboard shortcut (only for command macro names)');
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
'Length of the name (character count)');
lenName := b;
numbytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(b),
'Size of the formula data');
formulaSize := b;
end
else
begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
macro := (w and $0008 <> 0);
builtinName := (w and $0020 <> 0);
FDetails.Add('Option flags:'#13);
if w and $0001 = 0
then FDetails.Add(' Bit $0001 (flag name "hidden") = 0: Visible')
else FDetails.Add('* Bit $0001 (flag name "hidden")= 1: Hidden');
if w and $0002 = 0
then FDetails.Add(' Bit §0002 (flag name "func") = 0: Command macro')
else FDetails.Add('* Bit $0002 (flag name "func") = 1: Function macro');
if w and $0004 = 0
then FDetails.Add(' Bit $0004 (flag name "vbasic") = 0: Sheet macro')
else FDetails.Add('* Bit $0004 (flag name "vbasic") = 1: Visual basic macro');
if w and $0008 = 0
then FDetails.Add(' Bit $0008 (flag name "macro") = 0: Standard name')
else FDetails.Add('* Bit $0008 (flag name "macro") = 1: Macro name');
if w and $0010 = 0
then FDetails.Add(' Bit $0010 (flag name "complex") = 0: Simple formula')
else FDetails.Add('* Bit $0010 (flag name "complex") = 1: Complex formula (array formula or user defined)');
if w and $0020 = 0
then FDetails.Add(' Bit $0020 (flag name "builtin") = 0: User-defined name')
else FDetails.Add('* Bit $0020 (flag name "builtin") = 1: Built-in name');
case (w and $0FC0) shr 6 of
0: if macro then
FDetails.Add(' Bit $0FC0 = 0: --- forbidden value, must be > 0 ---')
else
FDetails.Add(' Bit $0FC0 (flag name "funcgroup") = 0: not used (requires "macro" = 1)');
1: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 1: financial');
2: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 2: date & time');
3: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 3: math & trig');
4: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 4: statistical');
5: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 5: lookup & reference');
6: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 6: database');
7: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 7: text');
8: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 8: logical');
9: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 9: information');
10: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 10: commands');
11: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 11: customizing');
12: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 12: macro control');
13: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 13: dde/external');
14: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 14: user defined');
end;
if w and $1000 = 0
then FDetails.Add(' Bit $1000 (flag name "binary") = 0: formula definition')
else FDetails.add('* Bit $1000 (flag name "binary") = 1: binary data (BIFF5-BIFF8)');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
'Option flags');
numbytes := 1;
b := FBuffer[FBufferIndex];
ShowInrow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b, b]),
'Keyboard shortcurt (only for command macro names)');
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
'Length of the name (character count)');
lenName := b;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'Size of the formula data');
formulaSize := w;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if FFormat = sfExcel5 then
ShowInRow(FCurrRow, FBufferIndex, NumBytes, IntToStr(w),
'0 = Global name, otherwise index to EXTERNSHEET record (one-based)')
else
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'not used');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, NumBytes, IntToStr(w),
'0 = Global name, otherwise index to sheet (one-based)');
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, nuMbytes, IntToStr(b),
'Length of the menu text (character count)');
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, nuMbytes, IntToStr(b),
'Length of the description text (character count)');
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, nuMbytes, IntToStr(b),
'Length of the help topic text (character count)');
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, nuMbytes, IntToStr(b),
'Length of the status bar text (character count)');
if FFormat = sfExcel5 then begin
numBytes := lenName * sizeOf(ansiChar);
SetLength(ansiStr, lenName);
Move(FBuffer[FBufferIndex], ansiStr[1], numbytes);
s := CP1252ToUTF8(ansistr);
end else
begin
if (FBuffer[FBufferIndex] and $01 = 0) //and (not IgnoreCompressedFlag)
then begin // compressed --> 1 byte per character
SetLength(ansiStr, lenName);
numbytes := lenName*SizeOf(ansiChar) + 1;
Move(FBuffer[FBufferIndex + 1], ansiStr[1], lenName*SizeOf(AnsiChar));
s := CP1252ToUTF8(ansiStr);
end else begin
SetLength(wideStr, lenName);
numBytes := lenName*SizeOf(WideChar) + 1;
Move(FBuffer[FBufferIndex + 1], wideStr[1], lenName*SizeOf(WideChar));
s := UTF8Encode(WideStringLEToN(wideStr));
end;
end;
if builtinName and (Length(s) = 1) then begin
s := Format('%s ($%x --> ', [s, ord(s[1])]);
case ord(s[1]) of
0: s := s + 'Consolidate_Area)';
1: s := s + 'Auto_Open)';
2: s := s + 'Auto_Close)';
3: s := s + 'Extract)';
4: s := s + 'Database)';
5: s := s + 'Citeria)';
6: s := s + 'Print_Area)';
7: s := s + 'Print_Titles)';
8: s := s + 'Recorder)';
9: s := s + 'Data_Form)';
10: s := s + 'Auto_Activate)';
11: s := s + 'Auto_Deactivate)';
12: s := s + 'Sheet_Title)';
13: s := s + '_FilterDatabase)';
else s := s + 'unknown meaning)';
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
'Name (Unicode string without length field)');
end;
firstTokenBufIdx := FBufferIndex;
while FBufferIndex < firstTokenBufIdx + formulaSize do begin
token := FBuffer[FBufferIndex];
numBytes := 1;
case token of
$10:
begin
numBytes := 1;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token tList (List operator)');
end;
$29, $39, $49:
begin
case token of
$29: s := 'Token tMemFuncR';
$39: s := 'Token tMemFuncV';
$49: s := 'Token tMemFuncV';
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [token]), s);
if FFormat = sfExcel2 then
begin
numbytes := 1;
b := FBuffer[FBufferIndex];
w := b;
end else
begin
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
'Size of following subexpression (ends after offset ' + IntToStr(FBufferIndex+w-1+numbytes) + ')');
end;
$3A, $3B, $5A, $5B, $7A, $7B:
begin
case token of
$3A: s := 'Token tRef3dR for "3D or external reference to a cell" (R = Reference)';
$5A: s := 'Token tRef3dV for "3D or external reference to a cell" (V = Value)';
$7A: s := 'Token tRef3dA for "3D or external reference to a cell" (A = Area)';
$3B: s := 'Token tArea3dR for "3D or external reference to a cell range" (R = Reference)';
$5B: s := 'Token tArea3dV for "3D or external reference to a cell range" (V = Value)';
$7B: s := 'Token tArea3dA for "3D or external reference to a cell range" (A = Area)';
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [token]), s);
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if FFormat = sfExcel5 then begin
if w and $8000 <> 0 then begin // negative value --> 3D reference
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(SmallInt(w)),
'negative --> 3D reference, 1-based index to EXTERNSHEET record = ' + IntToStr(-SmallInt(w)));
numBytes := 8;
ShowInRow(FCurrRow, FBufferIndex, numBytes, '', 'Not used');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Zero-based index to first referenced sheet ($FFFF = deleted sheet)');
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Zero-based index to last referenced sheet ($FFFF = deleted sheet)');
end else
begin
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'External reference, 1-based index to EXTERNSHEET record');
numBytes := 12;
ShowInRow(FCurrRow, FBufferIndex, numBytes, '', 'Not used');
end;
end else
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'Index to REF entry in EXTERNSHEET record');
if token in [$3A, $5A, $7A] then
ShowCellAddress // Cell address
else
ShowCellAddressRange(FFormat); // Cell range
end;
else
numBytes := 1;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'(unknown token)');
end; // case
end; // while
RowCount := FCurrRow;
EndUpdate(true);
end;
procedure TBIFFGrid.ShowDefRowHeight;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + IfThen(FFormat = sfExcel2, 1, 2);
if FFormat = sfExcel2 then begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Default height for unused rows:'#13);
FDetails.Add(Format(
'Bits $7FFF = %d: Default height for unused rows, in twips = 1/20 of a point',
[w and $7FFF]));
if w and $8000 = 0 then
FDetails.Add('Bit $8000 = 0: Row height changed manually')
else
FDetails.Add('Bit $8000 = 1: Row height not changed manually');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x) = %.1fpt', [w, w, w/20]),
'Default height for unused rows, in twips = 1/20 of a point');
end else begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Option flags:'#13);
if w and $0001 = 0
then FDetails.Add('Bit $0001 = 0: Row height and default font height do match')
else FDetails.Add('Bit $0001 = 1: Row height and default font height do not match');
if w and $0002 = 0
then FDetails.Add('Bit $0002 = 0: Row is visible')
else FDetails.Add('Bit $0002 = 1: Row is hidden');
if w and $0004 = 0
then FDetails.Add('Bit $0004 = 0: No additional space above the row')
else FDetails.Add('Bit $0004 = 1: Additional space above the row');
if w and $0008 = 0
then FDetails.Add('Bit $0008 = 0: No additional space below the row')
else FDetails.Add('Bit $0008 = 1: Additional space below the row');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [WordLEToN(w)]),
'Option flags');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x) = %.1fpt', [w, w, w/20]),
'Default height for unused rows, in twips = 1/20 of a point');
end;
end;
procedure TBIFFGrid.ShowDelta;
var
numBytes: Integer;
dbl: Double;
begin
RowCount := FixedRows + 1;
numBytes := 8;
Move(FBuffer[FBufferIndex], dbl, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl),
'Maximum change in iteration (IEEE 754 floating-point value, 64-bit double precision)');
end;
procedure TBIFFGrid.ShowDimensions;
var
numBytes: Integer;
dw: DWord;
w: Word;
begin
RowCount := FixedRows + IfThen(FFormat = sfExcel2, 4, 5);
if FFormat = sfExcel8 then begin
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(DWordLEToN(dw)),
'Index to first used row');
Move(FBuffer[FBufferIndex], dw, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(DWordLEToN(dw)),
'Index to last used row, increased by 1');
end else begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index to first used row');
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index to last used row, increased by 1');
end;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index to first used column');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index to last used column, increased by 1');
if FFormat <> sfExcel2 then begin
numBytes := 2;
ShowInRow(FCurrRow, FBufferIndex, numBytes, '',
'(not used)');
end;
end;
procedure TBIFFGrid.ShowDSF;
var
w: Word;
numbytes: Integer;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), 'Reserved, MUST be ignored');
end;
procedure TBIFFGrid.ShowEOF;
begin
RowCount := FixedRows + 1;
ShowInRow(FCurrRow, FBufferIndex, 0, '', '(no content)');
end;
procedure TBIFFGrid.ShowExcel9File;
begin
RowCount := FixedRows + 1;
ShowInRow(FCurrRow, FBufferIndex, 0, '', 'Optional and unused');
end;
procedure TBIFFGrid.ShowExternBook;
var
numBytes: Integer;
w: Word;
wideStr: WideString;
ansiStr: AnsiString;
s: String;
i, n: Integer;
b: Byte;
rtfRuns: TRichTextFormattingRuns;
begin
BeginUpdate;
RowCount := FixedRows + 1000;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
n := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(n),
'Number of sheet names / number of sheets');
if Length(FBuffer) - FBufferIndex = 2 then begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
SetLength(ansiStr, 1);
ansiStr[1] := char((w and $FF00) shr 8);
s := Format('%s (string bytes <#%.2x> <#%.2x>)', [ansistr, w and $00FF, (w and $FF00) shr 8]);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s,
'(relict of BIFF5)');
end else begin
ExtractString(FBufferIndex, 2, true, s, numBytes);
if Row = FCurrRow then begin
FDetails.Add('Encoded URL without sheet name:'#13);
case s[1] of
#0: FDetails.Add('First character = #00: Reference relative to current sheet');
#1: FDetails.Add('First character = #01: Encoded URL follows');
#2: if FFormat = sfExcel8 then
FDetails.Add('First character = #02: Reference to a sheet in own document; sheet name follows')
else
FDetails.Add('First character = #02: Reference to the corrent sheet (nothing will follow)');
#3: if FFormat = sfExcel5 then
FDetails.Add('First character = #03: Reference to a sheet in own document; sheet name follows')
else
FDetails.Add('First character = #03: not used');
#4: if FFormat = sfExcel5 then
FDetails.ADd('First character = #03: Reference to the own workbook, sheet is unspecified (nothing will follow)')
else
FDetails.Add('First character = #03: not used');
end;
end;
if s[1] in [#0, #1, #2, #3, #4] then Delete(s, 1, 1);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s,
'Encoded URL without sheet name (Unicode string, 16-bit string length)');
for i:=0 to n-1 do begin
ExtractString(FBufferIndex, 2, true, s, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s,
'Sheet name (Unicode string with 16-bit string length)');
end;
end;
RowCount := FCurrRow;
EndUpdate(true);
end;
procedure TBIFFGrid.ShowExternCount;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Number of following EXTERNSHEET records');
end;
procedure TBIFFGrid.ShowExternSheet;
var
numBytes: Integer;
w: Word;
s: String;
nREF: Integer;
i: Integer;
len: Byte;
ansiStr: AnsiString;
begin
if FFormat <= sfExcel5 then begin
RowCount := FixedRows + 1;
len := FBuffer[0];
if FBuffer[1] = $03 then inc(len);
numBytes := len*SizeOf(AnsiChar) + 1;
SetLength(ansiStr, len);
Move(FBuffer[1], ansiStr[1], len*SizeOf(AnsiChar));
s := CP1252ToUTF8(ansiStr);
if FCurrRow = Row then begin
FDetails.Add('Encoded document and sheet name:'#13);
if s[1] = #03 then begin
FDetails.Add('First character = $03: EXTERNSHEET stores a reference to one of the own sheets');
FDetails.Add('Document name: ' + Copy(s, 2, Length(s)));
end else
if (Length(s) = 1) and (s[1] = ':') then begin
FDetails.Add('Special EXTERNSHEET record for an add-in function. EXTERNName record with the name of the function follows.');
end else
FDetails.Add('Document name: ' + s);
end;
if s[1] = #03 then begin
Delete(s, 1, 1);
s := '<#03>' + s;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, s,
'Encoded document and sheet name (Byte string, 8-bit string length)');
end else begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
nREF := WordLEToN(w);
RowCount := FixedRows + 1 + nREF*3;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(nREF),
'Number of following REF structures');
for i:=1 to nREF do begin
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
Format('REF #%d: Index to EXTERNBOOK record', [i]));
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
Format('REF #%d: Index to first sheet in EXTERNBOOK sheet list', [i]));
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
Format('REF #%d: Index to last sheet in EXTERNBOOK sheet list', [i]));
end;
end;
end;
procedure TBIFFGrid.ShowFileSharing;
var
numbytes: Integer;
w: Word;
s: String;
begin
RowCount := FixedRows + 3;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Recommend read-only state when loading the file:'#13);
if w = 0 then FDetails.Add('0 = no') else FDetails.Add('1 = yes');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'Recommend read-only state when loading the file');
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
'Hash value calculated from the read-only password');
ExtractString(FBufferIndex, IfThen(FFormat=sfExcel8, 2, 1), FFormat=sfExcel8,
s, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
'User name of the file creator' + IfThen(FFormat = sfExcel8,
' (Unicode string, 16-bit string length)',
' (byte string, 8-bit string length)'
));
end;
procedure TBIFFGrid.ShowFnGroupCount;
var
numbytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Number of built-in function categories:'#13);
case w of
$000E:
FDetails.Add(
'There are 14 built-in function categories in the workbook.'#13+
'This implies that the file was last saved by a specific version of the application.'#13+
'The following 9 built-in function categories are visible to the end-user:'#13+
' Financial'#13+
' Date & Time'#13+
' Math & Trig'#13+
' Statistical'#13+
' Lookup & Reference'#13+
' Database'#13+
' Text'#13+
' Logical'#13+
' Information'#13+
'The following 5 built-in function categories are not visible to the end-user:'#13+
' UserDefined'#13+
' Commands'#13+
' Customize'#13+
' MacroControl'#13+
' DDEExternal'
);
$0010:
FDetails.Add(
'There are 16 built-in function categories in the workbook.'#13+
'This implies that the file was last saved by a specific version of the application'#13+
'The following 11 built-in function categories are visible to the end-user:'#13+
' Financial'#13+
' Date & time'#13+
' Math & Trig'#13+
' Statistical'#13+
' Lookup & Reference'+
' Database'#13+
' Text'#13+
' Logical'#13+
' Information'#13+
' Engineering'#13+
' Cube'#13+
'The following 5 built-in function categories are not visible to the end-user:'#13+
' UserDefined'#13+
' Commands'#13+
' Customize'#13+
' MacroControl'#13+
' DDEExternal'
);
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
'Number of built-in function categories');
end;
procedure TBIFFGrid.ShowFont;
var
numbytes: Integer;
w: Word;
b: Byte;
s: String;
begin
RowCount := IfThen(FFormat = sfExcel2, 3, 10) + FixedRows;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d (= %.1gpt)', [w, w/20]),
'Font height in twips (=1/20 point)');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Option flags:'#13);
if w and $0001 = 0
then FDetails.Add(' Bit $0001 = 0: not bold')
else FDetails.Add('x Bit $0001 = 1: bold (redundant in BIFF5-BIFF8)');
if w and $0002 = 0
then FDetails.Add(' Bit $0002 = 0: not italic')
else FDetails.Add('x Bit $0002 = 1: italic');
if w and $0004 = 0
then FDetails.Add(' Bit $0004 = 0: not underlined')
else FDetails.Add('x Bit $0004 = 1: underlined (redundant in BIFF5-BIFF8)');
if w and $0008 = 0
then FDetails.Add(' Bit $0008 = 0: not struck out')
else FDetails.Add('x Bit $0008 = 1: struck out');
if w and $0010 = 0
then FDetails.Add(' Bit $0010 = 0: not outlined')
else FDetails.Add('x Bit $0010 = 1: outlined');
if w and $0020 = 0
then FDetails.Add(' Bit $0020 = 0: not shadowed')
else FDetails.Add('x Bit $0020 = 1: shadowed');
if w and $0040 = 0
then FDetails.Add(' Bit $0040 = 0: not condensed')
else FDetails.Add('x Bit $0040 = 1: condensed');
if w and $0080 = 0
then FDetails.Add(' Bit $0080 = 0: not extended')
else FDetails.Add('x Bit $0080 = 1: extended');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
'Option flags');
if FFormat <> sfExcel2 then begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Color index');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w,w]),
'Font weight (400=normal, 700=bold)');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Escapement:'#13);
case w of
0: FDetails.Add('0 = none');
1: FDetails.Add('1 = superscript');
2: FDetails.Add('2 = subscript');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
'Escapement ($00=none, $01=superscript, $02=subscript)');
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
if Row = FCurrRow then begin
FDetails.Add('Underline type:'#13);
case b of
$00: FDetails.Add('$00 = no underline');
$01: FDetails.Add('$01 = single underline');
$02: FDetails.Add('$02 = double underline');
$21: FDetails.Add('$21 = single accounting');
$22: FDetails.Add('$22 = double accounting');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]),
'Underline type ($00=none, $01=single, $02=double, ...)');
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
if Row = FCurrRow then begin
FDetails.Add('Font family:'#13);
case b of
$00: FDetails.Add('$00 = None (unknown or don''t care)');
$01: FDetails.Add('$01 = Roman (variable width, serifed)');
$02: FDetails.Add('$02 = Swiss (variable width, sans-serifed)');
$03: FDetails.Add('$03 = Modern (fixed width, serifed or sans-serifed)');
$04: FDetails.Add('$04 = Script (cursive)');
$05: FDetails.Add('$05 = Decorative (specialised, for example Old English, Fraktur)');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%.2x', [b]),
'Font family');
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
case b of
$00: s := 'ANSI Latin';
$01: s := 'System default';
$02: s := 'Symbol';
$4D: s := 'Apple Roman';
$80: s := 'ANSI Japanese Shift-JIS';
$81: s := 'ANSI Korean (Hangul)';
$82: s := 'ANSI Korean (Johab)';
$86: s := 'ANSI Chinese Simplified GBK';
$88: s := 'ANSI Chinese Traditional BIG5';
$A1: s := 'ANSI Greek';
$A2: s := 'ANSI Turkish';
$A3: s := 'ANSI Vietnamese';
$B1: s := 'ANSI Hebrew';
$B2: s := 'ANSI Arabic';
$BA: s := 'ANSI Baltic';
$CC: s := 'ANSI Cyrillic';
$DE: s := 'ANSI Thai';
$EE: s := 'ANSI Latin II (Central European)'; // East Europe in MS docs!
$FF: s := 'OEM Latin I';
else s := '';
end;
if s <> '' then s := Format('$%.2x: %s', [b, s]) else s := Format('$%.2x', [b]);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s,
'Character set');
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), 'Not used');
end;
ExtractString(FBufferIndex, 1, FFormat=sfExcel8, s, numbytes);
if FFormat = sfExcel8 then
ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Font name (unicode string, 8-bit string length)')
else
ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Font name (byte string, 8-bit string length)');
end;
procedure TBIFFGrid.ShowFontColor;
var
numBytes: Integer;
w: Word;
s: String;
begin
RowCount := FixedRows + 1;
NumBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
case w of
$0000: s := 'EGA Black (rgb = $000000)';
$0001: s := 'EGA White (rgb = $FFFFFF)';
$0002: s := 'EGA Red (rgb = $0000FF)';
$0003: s := 'EGA Green (rgb = $00FF00)';
$0004: s := 'EGA Blue (rgb = $FF0000)';
$0005: s := 'EGA Yellow (rgb = $00FFFF)';
$0006: s := 'EGA Magenta (rgb = $FF00FF)';
$0007: s := 'EGA Cyan (rgb = $FFFF00)';
$7FFF: s := 'Automatic (system window text colour)';
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.04x)', [w, w]),
Format('Font color index into preceding FONT record (%s)', [s]));
end;
procedure TBIFFGrid.ShowFooter;
var
numbytes: Integer;
s: String;
begin
RowCount := FixedRows + 1;
if Length(FBuffer) = 0 then
ShowInRow(FCurrRow, FBufferIndex, 0, '', '(empty record)')
else
begin
ExtractString(FBufferIndex, IfThen(FFormat=sfExcel8, 2, 1), FFormat=sfExcel8,
s, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
'Page footer string' + IfThen(FFormat = sfExcel8,
' (Unicode string, 16-bit string length)',
' (byte string, 8-bit string length)'
));
end;
end;
procedure TBIFFGrid.ShowFormat;
var
numBytes: Integer;
w: word;
b: Byte;
s: String;
begin
RowCount := IfThen(FFormat = sfExcel2, FixedRows + 1, FixedRows + 2);
if FFormat <> sfExcel2 then begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
'FormatIndex used in other records');
end;
b := IfThen(FFormat=sfExcel8, 2, 1);
ExtractString(FBufferIndex, b, (FFormat=sfExcel8), s, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s,
Format('Number format string (%s string, %d-bit string length)', [GetStringType, b*8]));
end;
procedure TBIFFGrid.ShowFormatCount;
var
numBytes: Integer;
w: Word;
begin
if FFormat = sfExcel2 then begin
RowCount := 1 + FixedRows;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Number of FORMAT records');
end;
end;
procedure TBIFFGrid.ShowFormula;
var
numBytes: Integer;
b: Byte;
w: Word;
q: QWord;
dbl: double absolute q;
bytearr: array[0..7] of byte absolute q;
wordarr: array[0..3] of word absolute q;
// s: String;
tokenBytes: Integer;
// firstTokenBufIdx: Integer;
// token: Byte;
r,c, r2,c2: Integer;
begin
BeginUpdate;
RowCount := FixedRows + 1000;
// Brute force simplification because of unknown row count at this point
// Will be reduced at the end.
// Offset 0 = Row, Offset 2 = Column
ShowRowColData(FBufferIndex);
// Offset 4 = Cell attributes (BIFF2) or XF ecord index (> BIFF2)
if FFormat = sfExcel2 then begin
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
if Row = FCurrRow then begin
FDetails.Add('Cell protection and XF index:'#13);
FDetails.Add(Format('x Bits 5-0 = %d: XF Index', [b and $3F]));
case b and $40 of
0: FDetails.Add(' Bit 6 = 0: Cell is NOT locked.');
1: FDetails.Add('x Bit 6 = 1: Cell is locked.');
end;
case b and $80 of
0: FDetails.Add(' Bit 7 = 0: Formula is NOT hidden.');
1: FDetails.Add('x Bit 7 = 1: Formula is hidden.');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
'Cell protection and XF index');
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
if Row = FCurrRow then begin
FDetails.Add('Indexes to format and font records:'#13);
FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f]));
FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6]));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
'Indexes of format and font records');
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
if Row = FCurrRow then begin
FDetails.Add('Cell style:'#13);
case b and $07 of
0: FDetails.Add('Bits 2-0 = 0: Horizontal alignment is GENERAL');
1: FDetails.Add('Bits 2-0 = 1: Horizontal alignment is LEFT');
2: FDetails.Add('Bits 2-0 = 2: Horizontal alignment is CENTERED');
3: FDetails.Add('Bits 2-0 = 3: Horizontal alignment is RIGHT');
4: FDetails.Add('Bits 2-0 = 4: Horizontal alignment is FILLED');
end;
if b and $08 = 0
then FDetails.Add('Bit 3 = 0: Cell has NO left border')
else FDetails.Add('Bit 3 = 1: Cell has left black border');
if b and $10 = 0
then FDetails.Add('Bit 4 = 0: Cell has NO right border')
else FDetails.Add('Bit 4 = 1: Cell has right black border');
if b and $20 = 0
then FDetails.Add('Bit 5 = 0: Cell has NO top border')
else FDetails.Add('Bit 5 = 1: Cell has top black border');
if b and $40 = 0
then FDetails.Add('Bit 6 = 0: Cell has NO bottom border')
else FDetails.Add('Bit 6 = 1: Cell has bottom black border');
if b and $80 = 0
then FDetails.Add('Bit 7 = 0: Cell has NO shaded background')
else FDetails.Add('Bit 7 = 1: Cell has shaded background');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]),
'Cell style');
end else begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrROw, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
'Index of XF record');
end;
// Offset 6: Result of formula
numBytes := 8;
Move(FBuffer[FBufferIndex], q, numBytes);
if wordarr[3] <> $FFFF then begin
if FCurrRow = Row then begin
FDetails.Add('Formula result:'#13);
FDetails.Add(Format('Bytes 0-7: $%.15x --> IEEE 764 floating-point value, 64-bit double precision'#13+
' = %g', [q, dbl]));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl),
'Result of formula (IEEE 764 floating-point value, 64-bit double precision)');
end else begin
case bytearr[0] of
0: begin // String result
if FCurrRow = Row then begin
FDetails.Add('Formula result:'#13);
FDetails.Add('Byte 0 = 0 --> Result is string, follows in STRING record');
FDetails.Add('Byte 1-5: Not used');
FDetails.Add('Byte 6&7: $FFFF --> no floating point number');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.16x', [q]),
'Result is a string, follows in STRING record');
end;
1: begin // BOOL result
if FCurrRow = Row then begin
FDetails.Add('Formula result:'#13);
FDetails.Add('Byte 0 = 1 --> Result is BOOLEAN');
FDetails.Add('Byte 1: Not used');
if bytearr[2] = 0
then FDetails.Add('Byte 2 = 0 --> FALSE')
else FDetails.Add('Byte 2 = 1 --> TRUE');
FDetails.Add('Bytes 3-5: Not used');
FDetails.Add('Bytes 6&7: $FFFF --> no floating point number');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.16x', [q]),
'Result is BOOLEAN');
end;
2: begin // ERROR result
if FCurrRow = Row then begin
FDetails.Add('Formula result:'#13);
FDetails.Add('Byte 0 = 2 --> Result is an ERROR value');
FDetails.Add('Byte 1: Not used');
case bytearr[2] of
$00: FDetails.Add('Byte 2 = $00 --> #NULL! Intersection of two cell ranges is empty');
$07: FDetails.Add('Byte 2 = $07 --> #DIV/0! Division by zero');
$0F: FDetails.Add('Byte 2 = $0F --> #VALUE! Wrong type of operand');
$17: FDetails.Add('Byte 2 = $17 --> #REF! Illegal or deleted cell reference');
$1D: FDetails.Add('Byte 2 = $1D --> #NAME? Wrong function or range name');
$24: FDetails.Add('Byte 2 = $24 --> #NUM! Value range overflow');
$2A: FDetails.Add('Byte 2 = $2A --> #N/A Argument or function not available');
end;
FDetails.Add('Bytes 6&7: $FFFF --> no floating point number');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.16x', [q]),
'Result is an ERROR value');
end;
3: begin // EMPTY cell
if FCurrRow = Row then begin
FDetails.Add('Formula result:'#13);
FDetails.Add('Byte 0 = 3 --> Result is an empty cell, for example an empty string');
FDetails.Add('Byte 1-5: Not used');
FDetails.Add('Bytes 6&7: $FFFF --> no floating point number');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.16x', [q]),
'Result is an EMPTY cell (empty string)');
end;
end;
end;
// Option flags
if FFormat = sfExcel2 then begin
numBytes := 1;
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Option flags:'#13);
case b of
0: FDetails.Add('0 = Do not recalculate');
1: FDetails.Add('1 = Recalculate always');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), 'Option flags');
end else begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Option flags:'#13);
if w and $0001 = 0
then FDetails.Add('Bit $0001 = 0: Do not recalculate')
else FDetails.Add('Bit $0001 = 1: Recalculate always');
FDetails.Add('Bit $0002: Reserved - MUST be zero, MUST be ignored');
if w and $0004 = 0
then FDetails.Add('Bit $0004 = 0: Cell does NOT have a fill alignment or a center-across-selection alignment.')
else FDetails.Add('Bit $0004 = 1: Cell has either a fill alignment or a center-across-selection alignment.');
if w and $0008 = 0
then FDetails.Add('Bit $0008 = 0: Formula is NOT part of a shared formula')
else FDetails.Add('Bit $0008 = 1: Formula is part of a shared formula');
FDetails.Add('Bit $0010: Reserved - MUST be zero, MUST be ignored');
if w and $0020 = 0
then FDetails.Add('Bit $0020 = 0: Formula is NOT excluded from formula error checking')
else FDetails.Add('Bit $0020 = 1: Formula is excluded from formula error checking');
FDetails.Add('Bits $FC00: Reserved - MUST be zero, MUST be ignored');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]),
'Option flags');
end;
// Not used
if (FFormat >= sfExcel5) then begin
numBytes := 4;
ShowInRow(FCurrRow, FBufferIndex, numBytes, '', '(not used');
end;
// Size of Token array (in Bytes)
if FFormat = sfExcel2 then begin
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
tokenBytes := b;
end else begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
tokenBytes := WordLEToN(w);
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(tokenBytes),
'Size of formula data (in Bytes)');
ShowFormulaTokens(tokenBytes);
RowCount := FCurrRow;
EndUpdate(true);
end;
procedure TBIFFGrid.ShowFormulaTokens(ATokenBytes: Integer);
var
numBytes: Integer;
b: Byte;
w: Word;
s: String;
dbl: Double;
firstTokenBufIndex: Integer;
token: Byte;
r, c: Word;
begin
// Tokens and parameters
firstTokenBufIndex := FBufferIndex;
while FBufferIndex < firstTokenBufIndex + ATokenBytes do begin
token := FBuffer[FBufferIndex];
numBytes := 1;
case token of
$01: begin
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [token]),
'Token for "Cell is part of shared formula"');
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index to row of first FORMULA record in the formula range');
if FFormat = sfExcel2 then begin
numbytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
'Index to column of first FORMULA record in the formula range');
end else begin
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index to column of first FORMULA record in the formula range');
end;
end;
$02: begin
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [token]),
'Token for "Cell is part of a multiple operations table"');
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index to first row of the table range');
if FFormat = sfExcel2 then begin
numbytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
'Index to first column of the table range');
end else begin
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index to first column of the table range');
end;
end;
$03: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token "+" (add)');
$04: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token "-" (subtract)');
$05: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token "*" (multiply)');
$06: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token "/" (divide)');
$07: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token "^" (power)');
$08: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token "&" (concat)');
$09: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token "<" (less than)');
$0A: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token "<=" (less equal)');
$0B: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token "=" (equal)');
$0C: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token ">=" (greater equal)');
$0D: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token ">" (greater than)');
$0E: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token "<>" (not equal)');
$0F: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token " " (intersect)');
$10: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token "list character"');
$11: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token ":" (range)');
$12: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token "+" (unary plus)');
$13: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token "-" (unary minus)');
$14: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token "%" (percent)');
$15: ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [token]),
'Token "()" (operator in parenthesis)');
$16: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token "missing argument"');
$17: begin
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token tSTR (Label)');
ExtractString(FBufferIndex, 1, (FFormat = sfExcel8), s, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'String value');
end;
$1C: begin
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token tERR (Error)');
numBytes := 1;
b := FBuffer[FBufferIndex];
if FCurrRow = Row then begin
FDetails.Add('Error code:'#13);
FDetails.Add(Format('Code $%.2x --> "%s"', [b, GetErrorValueStr(TsErrorValue(b))]));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), 'Error code');
end;
$1D: begin
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token tBOOL');
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
'0=FALSE, 1=TRUE');
end;
$1E: begin
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token tINT (Integer)');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Integer value');
end;
$1F: begin
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token tNUM (Number)');
numBytes := 8;
Move(FBuffer[FBufferIndex], dbl, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%g', [dbl]), //FloatToStr(dbl),
'IEEE 754 floating-point value');
end;
$20, $40, $60:
begin
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token tARRAY');
if FFormat = sfExcel2 then numBytes := 6 else numBytes := 7;
ShowInRow(FCurrRow, FBufferIndex, numbytes, '', '(not used)');
end;
$21, $41, $61:
begin
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token tFUNC (Function with fixed argument count)');
if FFormat = sfExcel2 then begin
numBytes := 1;
b := FBuffer[FBufferIndex];
s := Format('Index of function (%s)', [SheetFuncName(b)]);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), s);
end else begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
s := Format('Index of function (%s)', [SheetFuncName(w)]);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), s);
end;
end;
$22, $42, $62:
begin
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token tFUNCVAR (Function with variable argument count)');
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
'Number of arguments');
if FFormat = sfExcel2 then begin
numBytes := 1;
s := Format('Index of built-in function (%s)', [SheetFuncName(b)]);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), s);
end else begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
s := Format('Index of built-in function (%s)', [SheetFuncName(w)]);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), s);
end;
end;
$23, $43, $63:
begin
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'Token tNAME');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
case FFormat of
sfExcel2: s := 'DEFINEDNAME or EXTERNALNAME record';
sfExcel5: s := 'DEFINEDNAME record in Global Link Table';
sfExcel8: s := 'DEFINEDNAME record in Link Table';
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'1-based index to '+s);
case FFormat of
sfExcel2: numBytes := 5;
sfExcel5: numBytes := 12;
sfExcel8: numBytes := 2;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, '', '(not used)');
end;
$24, $44, $64:
begin
case token of
$24: s := 'reference';
$44: s := 'value';
$64: s := 'array';
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
Format('Token tREF (Cell %s)', [s]));
ShowCellAddress;
end;
$25, $45, $65:
begin
case token of
$25: s := 'reference';
$45: s := 'value';
$65: s := 'array';
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
Format('Token tAREA (Cell range %s)', [s]));
ShowCellAddressRange(FFormat);
end;
$2C, $4C, $6C:
begin
case token of
$2C: s := 'reference';
$4C: s := 'value';
$6C: s := 'array';
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
Format('Token tREFN (Relative reference to cell %s in same sheet)', [s]));
// Encoded relative cell address
if FFormat = sfExcel8 then
begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes); // row
r := WordLEToN(w);
Move(FBuffer[FBufferIndex+2], w, numBytes); // column with flags
c := WordLEToN(w);
{ Note: The bitmask assignment to relative column/row is reversed in relation
to OpenOffice documentation in order to match with Excel files. }
if Row = FCurrRow then begin
FDetails.Add('Encoded cell address (row):'#13);
if c and $8000 = 0 then
begin
FDetails.Add('Row index is ABSOLUTE (see Encoded column index)');
FDetails.Add('Absolute row index: ' + IntToStr(r));
end else
begin
FDetails.Add('Row index is RELATIVE (see Encoded column index)');
FDetails.Add('Relative row index: ' + IntToStr(Smallint(r)));
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [r, r]),
'Encoded row index');
// Bit mask $4000 --> column
// Bit mask $8000 --> row
if Row = FCurrRow then
begin
FDetails.Add('Encoded cell address (column):'#13);
if c and $4000 = 0
then FDetails.Add('Bit 14=0: Column index is ABSOLUTE')
else FDetails.Add('Bit 14=1: Column index is RELATIVE');
if c and $8000 = 0
then FDetails.Add('Bit 15=0: Row index is ABSOLUTE')
else FDetails.Add('Bit 15=1: Row index is RELATIVE');
FDetails.Add('');
if c and $4000 = 0
then FDetails.Add('Absolute column index: ' + IntToStr(Lo(c)))
else FDetails.Add('Relative column index: ' + IntToStr(ShortInt(Lo(c))));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [c, c]),
'Encoded column index');
end
else
// Excel5 (Excel2 does not support shared formulas)
begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
r := w and $3FFF;
b := FBuffer[FBufferIndex+2];
c := b;
// Bit mask $4000 --> column
// Bit mask $8000 --> row
if Row = FCurrRow then begin
FDetails.Add('Encoded cell address (row):'#13);
if w and $4000 = 0
then FDetails.Add('Bit 14=0: Column index is ABSOLUTE')
else FDetails.Add('Bit 14=0: Column index is RELATIVE');
if w and $8000 = 0
then FDetails.Add('Bit 15=0: Row index is ABSOLUTE')
else FDetails.Add('Bit 15=1: Row index is RELATIVE');
FDetails.Add('');
if w and $8000 = 0
then FDetails.Add('Absolute row index: ' + IntToStr(r))
else FDetails.Add('Relative row index: ' + IntToStr(Smallint(r)));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
'Encoded row index');
if Row = FCurrRow then
begin
FDetails.Add('Encoded cell address (column):'#13);
if w and $4000 = 0 then begin
FDetails.Add('Column index is ABSOLUTE (see Encoded row index)');
FDetails.Add('Absolute column index: ' + IntToStr(c));
end else begin
FDetails.Add('Column index is RELATIVE (see Encoded row index)');
FDetails.Add('Relative column index: ' + IntToStr(ShortInt(c)));
end;
end;
numBytes := 1;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b, b]),
'Encoded column index');
end;
end;
else
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
'(unknown token)');
end; // case
end; // while
end;
procedure TBIFFGrid.ShowGCW;
var
numBytes: Integer;
b: Byte;
w: Word;
i,j: Integer;
bit: Byte;
begin
RowCount := FixedRows + 33;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'Size of Global Column Width bit field (1 bit per column), must be 32');
numBytes := 1;
for i:= 0 to w-1 do begin
b := FBuffer[FBufferIndex];
if FCurrRow = Row then begin
FDetails.Add(Format('GCW (Global column width) record, byte #%d:'#13, [i]));
bit := 1;
for j:=0 to 7 do begin
if b and bit = 0
then FDetails.Add(Format('Bit $%.2x=0: Column %d uses width of COLWIDTH record.', [bit, j+i*8]))
else FDetails.Add(Format('Bit $%.2x=1: Column %d uses width of STANDARDWIDTH record '+
'(or, if not available, DEFCOLWIDTH record)', [bit, j+i*8]));
bit := bit * 2;
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]),
Format('Widths of columns %d-%d', [i*8, i*8+7]));
end;
end;
procedure TBIFFGrid.ShowHeader;
var
numbytes: Integer;
s: String;
begin
RowCount := FixedRows + 1;
if Length(FBuffer) = 0 then
ShowInRow(FCurrRow, FBufferIndex, 0, '', '(empty record)')
else
begin
ExtractString(FBufferIndex, IfThen(FFormat=sfExcel8, 2, 1), FFormat=sfExcel8,
s, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
'Page header string' + IfThen(FFormat = sfExcel8,
' (Unicode string, 16-bit string length)',
' (byte string, 8-bit string length)'
));
end;
end;
procedure TBIFFGrid.ShowHideObj;
var
numBytes: word;
w: Word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Viewing mode for objects:'#13);
case w of
0: FDetails.Add('0 = Show all objects');
1: FDetails.Add('1 = Show placeholders');
2: FDetails.Add('2 = Do not show objects');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'Viewing mode for objects');
end;
procedure TBIFFGrid.ShowHyperlink;
var
numbytes: Word;
w: Word;
dw: DWord;
n: Integer;
guid: TGUID;
flags: DWord;
nchar, size: DWord;
widestr: WideString;
ansistr: ansistring;
s: String;
begin
n := 0;
RowCount := FixedRows + 1000;
ShowCellAddressRange(FFormat);
inc(n, 4);
numbytes := 16;
Move(FBuffer[FBufferIndex], guid, numbytes);
s := GuidToString(guid);
ShowInRow(FCurrRow, FBufferIndex, numbytes, GuidToString(guid), 'GUID of standard link');
inc(n);
numbytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
dw := DWordToLE(dw);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x (%d)', [dw, dw]), 'Unknown');
inc(n);
numbytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
flags := DWordToLE(dw);
if FCurrRow = row then begin
FDetails.Add('Option flags:'#13);
if flags and $0001 = 0
then FDetails.Add(' Bit $0001=0: No link')
else FDetails.ADd('* Bit $0001=1: File link or URL');
if flags and $0002 = 0
then FDetails.Add(' Bit $0002=0: Relative path')
else FDetails.Add('* Bit $0002=1: Absolute path');
if flags and $0014 = 0
then FDetails.Add(' Bits $0014=0: No desriptions')
else FDetails.Add('* Bits $0014=1: Description (both bits)');
if flags and $0008 = 0
then FDetails.Add(' Bit $0008=0: No text mark')
else FDetails.Add('* Bit $0008=1: Text mark');
if flags and $0080 = 0
then FDetails.Add(' Bit $0080=0: No target frame')
else FDetails.Add('* Bit $0080=1: Target frame');
if flags and $0100 = 0
then FDetails.Add(' Bit $0100=0: File link or URL')
else FDetails.Add('* Bit $0100=1: UNC path (incl server name)');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x (%d)', [flags, flags]),
'Option flags');
inc(n);
if flags and $0014 = $0014 then // hyperlink has description
begin
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
nchar := DWordToLE(dw);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(nchar),
'Character count of description text, incl trailing zero word');
inc(n);
numbytes := 2*nchar;
SetLength(widestr, nchar);
Move(FBuffer[FBufferIndex], widestr[1], 2*nchar);
s := UTF16ToUTF8(widestr);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
'Character array of description text (no unicode string header, always 16-bit characters, zero-terminated)');
inc(n);
end;
if flags and $0080 <> 0 then // Hyperlink has target frame
begin
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
nchar := DWordToLE(dw);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(nchar),
'Character count of target frame, incl trailing zero word');
inc(n);
numbytes := 2*nchar;
SetLength(widestr, nchar);
Move(FBuffer[FBufferIndex], widestr[1], 2*nchar);
s := UTF16ToUTF8(widestr);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
'Character array of target frame (no unicode string header, always 16-bit characters, zero-terminated)');
inc(n);
end;
if flags and $0011 <> 0 then // hyperlink contains URL ***OR*** a local file
begin
numbytes := 16;
Move(FBuffer[FBufferIndex], guid, numbytes);
s := GuidToString(guid);
if s = '{79EAC9E0-BAF9-11CE-8C82-00AA004BA90B}' then // case: URL
begin
ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
'GUID of URL Moniker');
inc(n);
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
size := DWordToLE(dw);
nchar := size div 2 - 1;
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(size),
'Size of URL character array, incl. trailing zero word');
inc(n);
numbytes := 2*nchar;
SetLength(widestr, nchar);
Move(FBuffer[FBufferIndex], widestr[1], 2*nchar);
s := UTF16ToUTF8(widestr);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
'Character array of the URL (no unicode string header, always 16-bit characters, zero-terminated');
inc(n);
end else
if s = '{00000303-0000-0000-C000-000000000046}' then // case: local file
begin
ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
'GUID of File Moniker');
inc(n);
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
'Directory up-level count. Each leading "..\" in the file link is deleted and increases this counter.');
inc(n);
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
nchar := DWordToLE(dw);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(nchar),
'Character count of the shortened file path and name, incl trailing zero byte.');
inc(n);
numbytes := nchar;
SetLength(ansistr, nchar);
Move(FBuffer[FBufferIndex], ansistr[1], nchar);
s := CP1252ToUTF8(ansistr);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
'Character array of the shortened file path and name (no unicode string header, always 8-bit characters, zero-terminated)');
inc(n);
for w := 1 to 6 do begin
numbytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [DWordToLE(dw)]),
'Unknown');
inc(n);
end;
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
size := DWordToLE(dw);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(size),
'Size of following file link field incl string length field and additional data field');
inc(n);
if size > 0 then begin
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
size := DWordToLE(dw);
nchar := size div 2;
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(size),
'Size of extended file path and name character array');
inc(n);
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordToLE(w)),
'Unknown');
inc(n);
numBytes := size;
SetLength(widestr, nchar);
Move(FBuffer[FBufferIndex], widestr[1], numbytes);
s := UTF16ToUTF8(widestr);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
'Character array of extended file path and array (No unicode string header, always 16-bit characters, NOT zero-terminated)');
inc(n);
end;
end;
end // if flags and $0011 <> 0
else begin
// case: Hyperlink to current workbook
end;
if flags and $0008 <> 0 then // hyperlink contains text mark field
begin
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
nchar := DWordToLE(dw);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(nchar),
'Character count of the text mark, incl trailing zero word');
inc(n);
numbytes := 2*nchar;
SetLength(widestr, nchar);
Move(FBuffer[FBufferIndex], widestr[1], 2*nchar);
s := UTF16ToUTF8(widestr);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
'Character array of the text mark, without "#" sign, no unicode string header, always 16-bit characters, zero-terminated)');
inc(n);
end;
RowCount := FixedRows + n;
end;
procedure TBIFFGrid.ShowHyperlinkTooltip;
var
numbytes: Word;
w: Word;
widestr: widestring;
s: string;
begin
RowCount := FixedRows + 6;
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordToLE(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x (%d)', [w, w]),
'Repeated record ID');
ShowCellAddressRange(sfExcel8);
numbytes := Length(FBuffer) - FBufferIndex;
SetLength(widestr, numbytes div 2);
Move(FBuffer[FBufferIndex], widestr[1], numbytes);
s := UTF16toUTF8(widestr);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
'Character array of the tool tip, no Unicode string header, always 16-bit characters, zero-terminated');
end;
procedure TBIFFGrid.ShowInRow(var ARow: Integer; var AOffs: LongWord;
ASize: Word; AValue,ADescr: String; ADescrOnly: Boolean = false);
begin
if ADescrOnly then
begin
Cells[0, ARow] := '';
Cells[1, ARow] := '';
Cells[2, ARow] := '';
end else
begin
Cells[0, ARow] := IntToStr(AOffs);
Cells[1, ARow] := IntToStr(ASize);
Cells[2, ARow] := AValue;
end;
Cells[3, ARow] := ADescr;
inc(ARow);
inc(AOffs, ASize);
end;
procedure TBIFFGrid.ShowInteger;
var
numBytes: Integer;
w: Word;
b: Byte;
begin
// BIFF2 only
if (FFormat <> sfExcel2) then
exit;
RowCount := FixedRows + 5;
ShowRowColData(FBufferIndex);
numBytes := 1;
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Cell protection and XF index:'#13);
FDetails.Add(Format('x Bits 5-0 = %d: XF Index', [b and $3F]));
case b and $40 of
0: FDetails.Add(' Bit 6 = 0: Cell is NOT locked.');
1: FDetails.Add('x Bit 6 = 1: Cell is locked.');
end;
case b and $80 of
0: FDetails.Add(' Bit 7 = 0: Formula is NOT hidden.');
1: FDetails.Add('x Bit 7 = 1: Formula is hidden.');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
'Cell protection and XF index');
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Indexes to format and font records:'#13);
FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f]));
FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6]));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
'Indexes of format and font records');
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Cell style:'#13);
case b and $07 of
0: FDetails.Add('x Bits 2-0 = 0: Horizontal alignment is GENERAL');
1: FDetails.Add('x Bits 2-0 = 1: Horizontal alignment is LEFT');
2: FDetails.Add('x Bits 2-0 = 2: Horizontal alignment is CENTERED');
3: FDetails.Add('x Bits 2-0 = 3: Horizontal alignment is RIGHT');
4: FDetails.Add('x Bits 2-0 = 4: Horizontal alignment is FILLED');
end;
if b and $08 = 0
then FDetails.Add(' Bit 3 = 0: Cell has NO left border')
else FDetails.Add('x Bit 3 = 1: Cell has left black border');
if b and $10 = 0
then FDetails.Add(' Bit 4 = 0: Cell has NO right border')
else FDetails.Add('x Bit 4 = 1: Cell has right black border');
if b and $20 = 0
then FDetails.Add(' Bit 5 = 0: Cell has NO top border')
else FDetails.Add('x Bit 5 = 1: Cell has top black border');
if b and $40 = 0
then FDetails.Add(' Bit 6 = 0: Cell has NO bottom border')
else FDetails.Add('x Bit 6 = 1: Cell has bottom black border');
if b and $80 = 0
then FDetails.Add(' Bit 7 = 0: Cell has NO shaded background')
else FDetails.Add('x Bit 7 = 1: Cell has shaded background');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]),
'Cell style');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Unsigned 16-bit integer cell value');
end;
procedure TBIFFGrid.ShowInterfaceEnd;
begin
RowCount := FixedRows + 1;
ShowInRow(FCurrRow, FBufferIndex, 0, '', 'End of Globals Substream');
end;
procedure TBIFFGrid.ShowInterfaceHdr;
var
numbytes: Integer;
w: Word;
begin
if FFormat < sfExcel8 then begin
RowCount := FixedRows;
exit;
end;
RowCount := FixedRows + 1;
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Code page of user interface:'#13);
FDetails.Add(Format('$%.4x = %s', [w, CodePageName(w)]));
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w, w]),
'Begin of Globals Substream, code page of user interface');
end;
procedure TBIFFGrid.ShowIteration;
var
numBytes: Integer;
w: word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Iterations:'#13);
case w of
0: FDetails.Add('0 = Iterations off');
1: FDetails.Add('1 = Iterations on');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Iterations on/off');
end;
procedure TBIFFGrid.ShowIXFE;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index to XF record');
end;
// Called for LABEL
procedure TBIFFGrid.ShowLabelCell;
var
numBytes: Integer;
b: Byte;
w: Word;
s: String;
begin
RowCount := IfThen(FFormat = sfExcel2, FixedRows + 6, FixedRows + 4);
ShowRowColData(FBufferIndex);
if (FFormat = sfExcel2) then begin
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
if Row = FCurrRow then begin
FDetails.Add('Cell protection and XF index:'#13);
FDetails.Add(Format('x Bits 5-0 = %d: XF Index', [b and $3F]));
case b and $40 of
0: FDetails.Add(' Bit 6 = 0: Cell is NOT locked.');
1: FDetails.Add('x Bit 6 = 1: Cell is locked.');
end;
case b and $80 of
0: FDetails.Add(' Bit 7 = 0: Formula is NOT hidden.');
1: FDetails.Add('x Bit 7 = 1: Formula is hidden.');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
'Cell protection and XF index');
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
if Row = FCurrRow then begin
FDetails.Add('Indexes to format and font records:'#13);
FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f]));
FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6]));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
'Indexes of format and font records');
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
if Row = FCurrRow then begin
FDetails.Add('Cell style:'#13);
case b and $07 of
0: FDetails.Add('Bits 2-0 = 0: Horizontal alignment is GENERAL');
1: FDetails.Add('Bits 2-0 = 1: Horizontal alignment is LEFT');
2: FDetails.Add('Bits 2-0 = 2: Horizontal alignment is CENTERED');
3: FDetails.Add('Bits 2-0 = 3: Horizontal alignment is RIGHT');
4: FDetails.Add('Bits 2-0 = 4: Horizontal alignment is FILLED');
end;
if b and $08 = 0
then FDetails.Add('Bit 3 = 0: Cell has NO left border')
else FDetails.Add('Bit 3 = 1: Cell has left black border');
if b and $10 = 0
then FDetails.Add('Bit 4 = 0: Cell has NO right border')
else FDetails.Add('Bit 4 = 1: Cell has right black border');
if b and $20 = 0
then FDetails.Add('Bit 5 = 0: Cell has NO top border')
else FDetails.Add('Bit 5 = 1: Cell has top black border');
if b and $40 = 0
then FDetails.Add('Bit 6 = 0: Cell has NO bottom border')
else FDetails.Add('Bit 6 = 1: Cell has bottom black border');
if b and $80 = 0
then FDetails.Add('Bit 7 = 0: Cell has NO shaded background')
else FDetails.Add('Bit 7 = 1: Cell has shaded background');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]),
'Cell style');
end else begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
'Index of XF record');
end;
b := IfThen(FFormat=sfExcel2, 1, 2);
ExtractString(FBufferIndex, b, (FFormat = sfExcel8), s, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s,
Format('%s string, %d-bit string length', [GetStringType, b*8]));
end;
procedure TBIFFGrid.ShowLabelSSTCell;
var
numBytes: Integer;
w: Word;
dw: DWord;
begin
RowCount := FixedRows + 4;
ShowRowColData(FBufferIndex);
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
'Index of XF record');
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numBytes);
dw := DWordLEToN(dw);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(dw),
'Index into SST record (shared string table)');
end;
procedure TBIFFGrid.ShowLeftMargin;
var
numBytes: Integer;
dbl: Double;
begin
RowCount := FixedRows + 1;
numBytes := 8;
Move(FBuffer[FBufferIndex], dbl, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl),
'Left page margin in inches (IEEE 754 floating-point value, 64-bit double precision)');
end;
procedure TBIFFGrid.ShowMergedCells;
var
w: Word;
numBytes: Integer;
i, n: Integer;
begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
n := WordLEToN(w); // count of merged ranges in this record
RowCount := FixedRows + 1 + n*4;
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(n),
'Count of merged ranges in this record');
for i:=1 to n do begin
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
Format('Merged range #%d: First row = %d', [i, w]));
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
Format('Merged range #%d: Last row = %d', [i, w]));
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
Format('Merged range #%d: First column = %d', [i, w]));
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
Format('Merged range #%d: Last column = %d', [i, w]));
end;
end;
procedure TBIFFGrid.ShowMMS;
var
w: Word;
numbytes: Integer;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), 'Reserved, MUST be ignored');
end;
{ Only those records needed for comments are deciphered. }
procedure TBIFFGrid.ShowMSODrawing;
var
n: Integer;
numBytes: Integer;
w: Word;
dw: DWord;
recType: Word;
recLen: DWord;
isContainer: Boolean;
indent: String;
level: Integer;
function PrepareIndent(ALevel: Integer): String;
var
i: Integer;
begin
Result := '';
for i := 1 to ALevel do Result := Result + ' ';
end;
procedure DoShowHeader(out ARecType: Word; out ARecLen: DWord;
out IsContainer: Boolean);
var
s: String;
instance: Word;
version: Word;
begin
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
IsContainer := (w and $000F = $000F);
if IsContainer and (FBufferIndex > 0) then
indent := PrepareIndent(level+1);
s := IfThen(IsContainer, '***** CONTAINER *****', '--- ATOM ---');
ShowInRow(FCurrRow, FBufferIndex, 0, '', indent + s, TRUE);
inc(n);
version := w and $000F;
instance := (w and $FFF0) shr 4;
if Row = FCurrRow then begin
FDetails.Add('OfficeArtDrawing Header:'#13);
FDetails.Add(Format('Bits 3-0 = $%.1x: Version', [version]));
FDetails.Add(Format('Bits 15-4 = $%.3x: Instance', [instance]));
end;
s := Format('OfficeArtDrawing Header: Version %d, instance %d', [version, instance]);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.04x', [w]), indent + s);
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ARecType := w;
case ARecType of
$F00A: //, $F00B:
case instance of
$01: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Rectangle)';
$02: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Rounded rectangle)';
$03: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Ellipse)';
$04: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Diamond)';
$05: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Triangle)';
$06: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Right triangle)';
$07: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Parallelogram)';
$08: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Trapezoid)';
$09: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Hexagon)';
$0A: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Pentagon)';
$0B: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Plus)';
$0C: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Star)';
$0D: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Arrow)';
$0E: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Thick arrow)';
$0F: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Irregular pentagon)';
$10: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Cube)';
$11: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Speech balloon)';
$12: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Seal)';
$13: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Curved arc)';
$14: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Line)';
$15: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Plaque)';
$16: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Cylinder)';
$17: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Donut)';
$CA: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Text box)';
end;
$F00B:
Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + Format(' (i.e., %d properties)', [instance]);
end;
case ARecType of
$F000: s := 'OfficeArtDggContainer (all the OfficeArt file records containing document-wide data)';
$F001: s := 'OfficeArtBStoreContainer (all the BLIPs used in all the drawings associated with parent OfficeArtDggContainer record)';
$F002: s := 'OfficeArtDgContainer (all file records for the objects in a drawing)';
$F003: s := 'OfficeArtSpgrContainer (groups of shapes)';
$F004: s := 'OfficeArtSpContainer (shape)';
$F005: s := 'OfficeArtSolverContainer (rules applicable to the shapes contained in an OfficeArtDgContainer record)';
$F006: s := 'OfficeArtFDGGBlock record (document-wide information about all drawings saved in the file)';
$F007: s := 'OfficeArtFBSE record (File BLIP Store Entry (FBSE) containing information about the BLIP)';
$F008: s := 'OfficeArtFDG record (number of shapes, drawing identifier, and shape identifier of the last shape in a drawing)';
$F009: s := 'OfficeArtFSPGR record (coordinate system of the group shape that the anchors of the child shape are expressed in)';
$F00A: s := 'OfficeArtFSP record (instance of a shape)';
$F00B: s := 'OfficeArtFOPT record (table of OfficeArtRGFOPTE records)';
$F00D: s := 'OfficeArtClientTextBox (text related data for a shape)';
$F00F: s := 'OfficeArtChildAnchor record (anchors for the shape containing this record)';
$F010: s := 'OfficeArtClientAnchor record (location of a shape)';
$F011: s := 'OfficeArtClientData record (information about a shape)';
$F012: s := 'OfficeArtFConnectorRule record (connection between two shapes by a connector shape)';
$F014: s := 'OfficeArtFArcRule record (Specifies an arc rule. Each arc shape MUST correspond to a unique arc rule)';
$F017: s := 'OfficeArtFCalloutRule record (Callout rule: One callout rule MUST exist per callout shape)';
$F01A: s := 'OfficeArtBlipEMF record (BLIP file data for the enhanced metafile format (EMF))';
$F01B: s := 'OfficeArtBlipWMF record (BLIP file data for the Windows Metafile Format (WMF))';
$F01C: s := 'OfficeArtBlipPICT record (BLIP file data for the Macintosh PICT format)';
$F01D: s := 'OfficeArtBlipJPEG record (BLIP file data for the Joint Photographic Experts Group (JPEG) format)';
$F01E: s := 'OfficeArtBlipPNG record (BLIP file data for the Portable Network Graphics (PNG) format)';
$F01F: s := 'OfficeArtBlipDIB record (BLIP file data for the device-independent bitmap (DIB) format)';
$F029: s := 'OfficeArtBlipTIFF record (BLIP file data for the TIFF format)';
$F118: s := 'OfficeArtFRITContainer record (container for the table of group identifiers that are used for regrouping ungrouped shapes)';
$F119: s := 'OfficeArtFDGSL record (specifies both the selected shapes and the shape that is in focus in the drawing)';
$F11A: s := 'OfficeArtColorMRUContainer record (most recently used custom colors)';
$F11D: s := 'OfficeArtFPSPL record (former hierarchical position of the containing object that is either a shape or a group of shapes)';
$F11E: s := 'OfficeArtSplitMenuColorContainer record (container for the colors that were most recently used to format shapes)';
$F121: s := 'OfficeArtSecondaryFOPT record (table of OfficeArtRGFOPTE records)';
$F122: s := 'OfficeArtTertiaryFOPT record (table of OfficeArtRGFOPTE records)';
else s := 'Not known to BIFFExplorer';
end;
if Row = FCurrRow then begin
FDetails.Add('OfficeArtDrawing Record Type:'#13);
FDetails.Add(Format('$%.4x: %s', [ARecType, s]));
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]),
indent + 'Record type: ' + s);
inc(n);
numbytes := 4;
Move(FBuffer[FbufferIndex], ARecLen, Numbytes);
ARecLen := DWordLEToN(ARecLen);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d', [ARecLen]),
indent + 'Record length');
inc(n);
end;
procedure DoShowOfficeArtFDG; // $F008
// The OfficeArtFDG record specifies the number of shapes, the drawing
// identifier, and the shape identifier of the last shape in a drawing.
begin
numbytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
dw := DWordLEToN(dw);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(dw),
indent + 'Number of shapes in this drawing');
inc(n);
Move(FBuffer[FBufferIndex], dw, numbytes);
dw := DWordLEToN(dw);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(dw),
indent + 'Shape identifier of the last shape in this drawing');
inc(n);
end;
procedure DoShowOfficeArtFSPGR; // $F009
// The OfficeArtFSPGR record specifies the coordinate system of the group
// shape that the anchors of the child shape are expressed in. This record
// is present only for group shapes.
var
rt: Word;
begin
numbytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(LongInt(DWordLEToN(dw))),
indent + 'xLeft');
inc(n);
Move(FBuffer[FBufferIndex], dw, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(LongInt(DWordLEToN(dw))),
indent + 'yTop');
inc(n);
Move(FBuffer[FBufferIndex], dw, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(LongInt(DWordLEToN(dw))),
indent + 'xRight');
inc(n);
Move(FBuffer[FBufferIndex], dw, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(LongInt(DWordLEToN(dw))),
indent + 'yBottom');
inc(n);
end;
procedure DoShowOfficeArtFSP; // §F00A
// The OfficeArtFSP record specifies an instance of a shape.
// The record header contains the shape type, and the record itself
// contains the shape identifier and a set of bits that further define the shape.
var
rt: word;
s: String;
begin
numbytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)),
indent + 'Shape identifier');
inc(n);
numbytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
dw := DWordLEToN(dw);
s := '';
if Row = FCurrRow then begin
FDetails.Add('Shape options:'#13);
if dw and $0001 = 0
then FDetails.Add(' Bit 1=0: Shape is NOT a group shape')
else FDetails.Add('x Bit 1=1: Shape is a group shape');
if dw and $0002 = 0
then FDetails.Add(' Bit 2=0: Shape is NOT a child shape')
else FDetails.Add('x Bit 2=1: Shape is a child shape');
if dw and $0004 = 0
then FDetails.Add(' Bit 3=0: Shape is NOT the top-most group shape (patriarch)')
else FDetails.Add('x Bit 3=1: Shaoe is the top-most group shape (patriarch)');
if dw and $0008 = 0
then FDetails.Add(' Bit 4=0: Shape has NOT been deleted')
else FDetails.Add('x Bit 4=1: Shape has been deleted');
if dw and $0010 = 0
then FDetails.Add(' Bit 5=0: Shape is NOT an OLE object')
else FDetails.Add('x Bit 5=1: Shape is an OLE object');
if dw and $0020 = 0
then FDetails.Add(' Bit 6=0: Shape does NOT have a valid master in the hspMaster property')
else FDetails.Add('x Bit 6=1: Shape has a valid master in the hspMaster property');
if dw and $0040 = 0
then FDetails.Add(' Bit 7=0: Shape is NOT horizontally flipped')
else FDetails.Add('x Bit 7=1: Shape is horizontally flipped');
if dw and $0080 = 0
then FDetails.Add(' Bit 8=0: Shape is NOT vertically flipped')
else FDetails.Add('x Bit 8=1: Shape is vertically flipped');
if dw and $0100 = 0
then FDetails.Add(' Bit 9=0: Shape is NOT a connector shape')
else FDetails.Add('x Bit 9=1: Shape is a connector shape');
if dw and $0200 = 0
then FDetails.Add(' Bit 10=0: Shape doe NOT have an anchor')
else FDetails.Add('x Bit 10=1: Shape has an anchor');
if dw and $0400 = 0
then FDetails.Add(' Bit 11=0: Shape is NOT a background shape')
else FDetails.Add('x Bit 11=1: Shape is a background shape');
if dw and $0800 = 0
then FDetails.Add(' Bit 12=0: Shape does NOT have a shape type property')
else FDetails.Add('x Bit 12=1: Shape has a shape type property');
FDetails.Add(' Bits 13-32: unused');
end;
s := '';
if dw and $0001 <> 0 then s := s + 'group shape, ';
if dw and $0002 <> 0 then s := s + 'child shape, ';
if dw and $0004 <> 0 then s := s + 'patriarch, ';
if dw and $0008 <> 0 then s := s + 'deleted, ';
if dw and $0010 <> 0 then s := s + 'OLE object, ';
if dw and $0020 <> 0 then s := s + 'master, ';
if dw and $0040 <> 0 then s := s + 'flipped hor, ';
if dw and $0080 <> 0 then s := s + 'flipped vert, ';
if dw and $0100 <> 0 then s := s + 'connector shape, ';
if dw and $0200 <> 0 then s := s + 'anchor, ';
if dw and $0400 <> 0 then s := s + 'background, ';
if dw and $0800 <> 0 then s := s + 'shape type, ';
if s <> '' then begin
Delete(s, Length(s)-1, 2);
s := 'Shape options: ' + s;
end else
s := 'Shape options';
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x (%d)', [dw, dw]), indent + s);
inc(n);
end;
procedure DoShowOfficeArtFOPT(ARecLen: DWord); // $F00B
// The OfficeArtFOPT record specifies a table of OfficeArtRGFOPTE records.
var
startIndex: Int64;
opid: Word;
op: DWord;
s: String;
begin
startIndex := FBufferIndex;
while FBufferIndex < startIndex + ARecLen do
begin
numbytes := 2;
Move(FBuffer[FBufferIndex], opid, numbytes);
opid := WordLEToN(opid);
case opid of
$007F: s := 'OfficeArtFOPTEOPID: Protection boolean properties';
$0080: s := 'OfficeArtFOPTEOPID: TextID';
$0081: s := 'OfficeArtFOPTEOPID: dxTextLeft';
$0082: s := 'OfficeArtFOPTEOPID: dyTextTop';
$0083: s := 'OfficeArtFOPTEOPID: dxTextRight';
$0084: s := 'OfficeArtFOPTEOPID: dyTextBottom';
$0085: s := 'OfficeArtFOPTEOPID: Wrap text';
$0086: s := 'OfficeArtFOPTEOPID: Unused';
$0087: s := 'OfficeArtFOPTEOPID: Text anchor';
$0088: s := 'OfficeArtFOPTEOPID: Text flow';
$0089: s := 'OfficeArtFOPTEOPID: Font rotation';
$008A: s := 'OfficeArtFOPTEOPID: Next shape in sequence of linked shapes';
$008B: s := 'OfficeArtFOPTEOPID: Text direction';
$008C: s := 'OfficeArtFOPTEOPID: Unused';
$008D: s := 'OfficeArtFOPTEOPID: Unused';
$00BF: s := 'OfficeArtFOPTEOPID: Boolean properties for the text in a shape';
$00C0: s := 'OfficeArtFOPTEOPID: Text for this shapes geometry text';
$00C2: s := 'OfficeArtFOPTEOPID: Alignment of text in the shape';
$00C3: s := 'OfficeArtFOPTEOPID: Font size, in points, of the geometry text for this shape';
$00C4: s := 'OfficeArtFOPTEOPID: Amount of spacing between characters in the text';
$00C5: s := 'OfficeArtFOPTEOPID: Font to use for the text';
$0158: s := 'OfficeArtFOPTEOPID: Type of connection point';
$0181: s := 'OfficeArtFOPTEOPID: Fill color';
$0183: s := 'OfficeArtFOPTEOPID: Background color of the fill';
$0185: s := 'OfficeArtFOPTEOPID: Foreground color of the fill';
$01BF: s := 'OfficeArtFOPTEOPID: Fill style boolean properties';
$01C0: s := 'OfficeArtFOPTEOPID: Line color';
$01C3: s := 'OfficeArtFOPTEOPID: Line foreground color for black & white display mode';
$01FF: s := 'OfficeArtFOPTEOPID: Line style boolean properties';
$0201: s := 'OfficeArtFOPTEOPID: Shadow color';
$0203: s := 'OfficeArtFOPTEOPID: Shadow primary color modifier if in black & white mode';
$023F: s := 'OfficeArtFOPTEOPID: Shadow style boolean properties';
$03BF: s := 'OfficeArtFOPTEOPID: Group shape boolean properties';
else s := 'OfficeArtFOPTEOPID that specifies the header information for this property';
end;
s := indent + s;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x (%d)', [opid, opid]), s);
inc(n);
numbytes := 4;
Move(FBuffer[FBufferIndex], op, numbytes);
op := DWordLEToN(op);
s := '';
case opid of
$0087: case op of
0: s := s + ': text at top';
1: s := s + ': text vertically centered';
2: s := s + ': text at bottom';
3: s := s + ': text at top and centered horizontally';
4: s := s + ': text at center of box';
5: s := s + ': text at bottom and centered horizontally';
end;
$0089: case op of
0: s := s + ': horizontal';
1: s := s + ': 90° clockwise, vertical down';
2: s := s + ': horizontal, 180° rotated';
3: s := s + ': 90° counter-clickwise, vertical up';
end;
$008B: case op of
0: s := s + ': left-to-right';
1: s := s + ': right-to-left';
2: s := s + ': determined from text string';
end;
$00C2: case op of
0: s := s + ': stretched';
1: s := s + ': centered';
2: s := s + ': left-aligned';
3: s := s + ': right-aligned';
4: s := s + ': justified';
5: s := s + ': word-justified';
end;
$0158: case op of
0: s := s + ': no connection point';
1: s := s + ': Edit points of shape are used as connection points';
2: s := s + ': Custom array of connection points';
3: s := s + ': standard 4 connection points at the top, bottom, left, and right side centers';
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x (%d)', [op, op]),
indent + 'Value of this property' + s);
inc(n);
end;
end;
procedure DoShowOfficeArtClientTextbox(ARecLen: Word); // $F00D
begin
numBytes := ARecLen;
ShowInRow(FCurrRow, FBufferIndex, numbytes, '...', indent + 'Text data follow in TXO record', true);
inc(n);
end;
procedure DoShowOfficeArtClientAnchorData(StructureKind: Integer); // $F010
begin
case StructureKind of
0: begin // OfficeArtClientAnchorSheet
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Move/resize flags:'#13);
if w and $0001 = 0
then FDetails.Add(' Bit 0=0: Shape will NOT be kept intact when the cells are moved')
else FDetails.Add('x Bit 0=1: Shape will be kept intact when the cells are moved');
if w and $0002 = 0
then FDetails.Add(' Bit 1=0: Shape will NOT be kept intact when the cells are resized')
else FDetails.Add('x Bit 1=1: Shape will be kept intact when the cells are resized');
FDetails.Add( ' Bit 2=0: reserved (must be 0)');
FDetails.Add( ' Bit 3=0: reserved (must be 0)');
FDetails.Add( ' Bit 4=0: reserved (must be 0)');
FDetails.Add( ' Bits 15-5: unused');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), indent + 'Move/resize flags');
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
indent + 'Column of the cell under the top left corner of the bounding rectangle of the shape');
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
indent + 'x coordinate of the top left corner of the bounding rectangle relative to the corner of the underlying cell (as 1/1024 of that cells width)');
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
indent + 'Row of the cell under the top left corner of the bounding rectangle of the shape');
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
indent + 'y coordinate of the top left corner of the bounding rectangle relative to the corner of the underlying cell (as 1/256 of that cells height');
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
indent + 'Column of the cell under the bottom right corner of the bounding rectangle of the shape');
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
indent + 'x coordinate of the bottom right corner of the bounding rectangle relative to the corner of the underlying cell (as 1/1024 of that cells width)');
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
indent + 'Row of the cell under the bottom right corner of the bounding rectangle of the shape');
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
indent + 'y coordinate of the bottom right corner of the bounding rectangle relative to the corner of the underlying cell (as 1/256 of that cells height');
inc(n);
end;
1: begin // "Small" rectangle structure
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
indent + 'top');
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
indent + 'left');
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
indent + 'right');
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
indent + 'bottom');
inc(n);
end;
2: begin // standard rectangle structure
numbytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)),
indent + 'top');
inc(n);
Move(FBuffer[FBufferIndex], dw, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)),
indent + 'left');
inc(n);
Move(FBuffer[FBufferIndex], dw, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)),
indent + 'right');
inc(n);
Move(FBuffer[FBufferIndex], dw, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)),
indent + 'bottom');
inc(n);
end;
end;
end;
procedure DoShowOfficeArtRecord(out ARecType:Word; out ARecLen: DWord;
out AIsContainer: Boolean);
var
startIndex, endindex: Int64;
totalbytes: Int64;
begin
totalbytes := 0;
startIndex := FBufferIndex + 8;
repeat
DoShowHeader(ARecType, ARecLen, AIsContainer);
if totalbytes = 0 then endindex := startindex + ARecLen;
totalbytes := totalbytes + ARecLen;
if AIsContainer then begin
inc(level);
indent := PrepareIndent(level);
DoShowOfficeArtRecord(ARecType, ARecLen, AIsContainer);
end else
case ARecType of
$F008: DoShowOfficeArtFDG;
$F009: DoShowOfficeArtFSPGR; // shape group
$F00A: DoShowOfficeArtFSP; // instance of a shape
$F00B: DoShowOfficeArtFOPT(ARecLen); // table of OfficeArtRGFOPTE records
$F00D: DoShowOfficeArtClientTextbox(ARecLen);
$F010: DoShowOfficeArtClientAnchorData(0); // 0 - use OfficeArtClientAnchorSheet because contained in a sheet stream
// $F122: DoShowOfficeArtRGFOPTE(AIndent); // (tertiary) table of OfficeArtRGFOPTE records
else if ARecLen <> 0 then begin
ShowInRow(FCurrRow, FBufferIndex, ARecLen, '', indent + 'Skipping this unknown record...');
inc(n);
end;
end;
until (FBufferIndex >= endindex) or (FBufferIndex >= Length(FBuffer));
dec(level);
FBufferIndex := endindex;
end;
begin
RowCount := FixedRows + 1000;
n := 0;
level := -1;
DoShowOfficeArtRecord(recType, recLen, isContainer);
RowCount := FixedRows + n;
end;
procedure TBIFFGrid.ShowMulBlank;
var
w: Word;
numbytes: Integer;
i, nc: Integer;
begin
nc := (Length(FBuffer) - 6) div 2;
RowCount := FixedRows + 3 + nc;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index to row');
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index to first column');
for i:=0 to nc-1 do begin
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
Format('Index to XF record #%d', [i]));
end;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index to last column');
end;
procedure TBIFFGrid.ShowMulRK;
var
w: Word;
numBytes: Integer;
i, nc: Integer;
dw: DWord;
encint: DWord;
encdbl: QWord;
dbl: Double absolute encdbl;
s: String;
begin
nc := (Length(FBuffer) - 6) div 6;
RowCount := FixedRows + 3 + nc*2;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index to row');
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index to first column');
for i:=0 to nc-1 do begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
Format('Index to XF record #%d', [i]));
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
dw := DWordLEToN(dw);
if Row = FCurrRow then begin
FDetails.Add('RK Value:'#13);
if dw and $00000001 = 0
then FDetails.Add('Bit 0 = 0: Value not changed')
else FDetails.Add('Bit 0 = 1: Encoded value is multiplied by 100.');
if dw and $00000002 = 0
then FDetails.Add('Bit 1 = 0: Floating point value')
else FDetails.Add('Bit 1 = 1: Signed integer value');
if dw and $00000002 = 0 then begin
encdbl := (QWord(dw) and QWord($FFFFFFFFFFFFFFFC)) shl 32;
if dw and $00000001 = 1 then
s := Format('%.2f', [dbl*0.01])
else
s := Format('%.0f', [dbl]);
end
else begin
s := Format('$%.16x', [-59000000]);
encint := ((dw and DWord($FFFFFFFC)) shr 2) or (dw and DWord($C0000000));
// "arithmetic shift" = replace left-most bits by original bits
if dw and $00000001 = 1 then
s := FloatToStr(encint*0.01)
else
s := IntToStr(encint);
end;
FDetails.Add('Bits 31-2: Encoded value ' + s);
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes,
Format('$%.8x', [dw]),
Format('RK value #%d', [i])
);
end;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index to last column');
end;
procedure TBIFFGrid.ShowNote;
var
numBytes: Integer;
w: Word = 0;
s: String;
begin
RowCount := IfThen(FFormat = sfExcel8, 6, 5);
// Offset 0: Row and Col index
ShowRowColData(FBufferIndex);
if FFormat = sfExcel8 then begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Comment flags:'#13);
if (w and $0002 <> 0)
then FDetails.Add('x Bit 1=1: Comment is shown at all times')
else FDetails.Add(' Bit 1=0: Comment is not shown at all tiems');
if (w and $0080 <> 0)
then FDetails.Add('x Bit 7=1: Row with comment is hidden')
else FDetails.Add(' Bit 7=0: Row with comment is visible');
if (w and $0100 <> 0)
then FDetails.Add('x Bit 8=1: Column with comment is hidden')
else FDetails.Add(' Bit 8=0: Column with comment is visible');
FDetails.Add('All other bits are reserved and must be ignored.');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
'Flags');
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Object ID');
ExtractString(FBufferIndex, IfThen(FFormat=sfExcel8, 2, 1), FFormat=sfExcel8,
s, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Author');
end else begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'Total length of comment');
numBytes := Min(Length(FBuffer) - FBufferIndex, 2048);
SetLength(s, numBytes);
Move(FBuffer[FBufferIndex], s[1], numBytes);
SetLength(s, Length(s));
s := UTF8StringReplace(s, #10, '[\n]', [rfReplaceAll]);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Comment text');
end;
end;
procedure TBIFFGrid.ShowNumberCell;
var
numBytes: Integer;
b: Byte = 0;
w: Word = 0;
dbl: Double;
begin
RowCount := IfThen(FFormat = sfExcel2, FixedRows + 6, FixedRows + 4);
// Offset 0: Row & Offsset 2: Column
ShowRowColData(FBufferIndex);
// Offset 4: Cell attributes (BIFF2) or XF ecord index (> BIFF2)
if FFormat = sfExcel2 then begin
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
if Row = FCurrRow then begin
FDetails.Add('Cell protection and XF index:'#13);
FDetails.Add(Format('x Bits 5-0 = %d: XF Index', [b and $3F]));
case b and $40 of
0: FDetails.Add(' Bit 6 = 0: Cell is NOT locked.');
1: FDetails.Add('x Bit 6 = 1: Cell is locked.');
end;
case b and $80 of
0: FDetails.Add(' Bit 7 = 0: Formula is NOT hidden.');
1: FDetails.Add('x Bit 7 = 1: Formula is hidden.');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
'Cell protection and XF index');
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
if Row = FCurrRow then begin
FDetails.Add('Indexes to format and font records:'#13);
FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f]));
FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6]));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
'Indexes of format and font records');
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
if Row = FCurrRow then begin
FDetails.Add('Cell style:'#13);
case b and $07 of
0: FDetails.Add('x Bits 2-0 = 0: Horizontal alignment is GENERAL');
1: FDetails.Add('x Bits 2-0 = 1: Horizontal alignment is LEFT');
2: FDetails.Add('x Bits 2-0 = 2: Horizontal alignment is CENTERED');
3: FDetails.Add('x Bits 2-0 = 3: Horizontal alignment is RIGHT');
4: FDetails.Add('x Bits 2-0 = 4: Horizontal alignment is FILLED');
end;
if b and $08 = 0
then FDetails.Add(' Bit 3 = 0: Cell has NO left border')
else FDetails.Add('x Bit 3 = 1: Cell has left black border');
if b and $10 = 0
then FDetails.Add(' Bit 4 = 0: Cell has NO right border')
else FDetails.Add('x Bit 4 = 1: Cell has right black border');
if b and $20 = 0
then FDetails.Add(' Bit 5 = 0: Cell has NO top border')
else FDetails.Add('x Bit 5 = 1: Cell has top black border');
if b and $40 = 0
then FDetails.Add(' Bit 6 = 0: Cell has NO bottom border')
else FDetails.Add('x Bit 6 = 1: Cell has bottom black border');
if b and $80 = 0
then FDetails.Add(' Bit 7 = 0: Cell has NO shaded background')
else FDetails.Add('x Bit 7 = 1: Cell has shaded background');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]),
'Cell style');
end else begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrROw, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
'Index of XF record');
end;
// Offset 6: Double value
numBytes := 8;
Move(FBuffer[FBufferIndex], dbl, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%g', [dbl]), //FloatToStr(dbl),
'IEEE 764 floating-point value');
end;
procedure TBIFFGrid.ShowObj;
var
numBytes: Integer;
w: Word;
dw: DWord;
savedBufferIndex: Integer;
fieldType: Word;
fieldSize: Word;
s: String;
i, n: Integer;
guid: TGUID;
begin
if FFormat = sfExcel8 then begin
n := 0;
RowCount := FixedRows + 1000;
while FBufferIndex < Length(FBuffer) do begin
savedBufferIndex := FBufferIndex;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
fieldType := WordLEToN(w);
case fieldType of
$00: s := 'ftEnd (End of OBJ record)';
$01: s := '(Reserved)';
$02: s := '(Reserved)';
$03: s := '(Reserved)';
$04: s := 'ftMacro (fmla-style macro)';
$05: s := 'ftButton (Command button)';
$06: s := 'ftGmo (Group marker)';
$07: s := 'ftCf (Clipboard format)';
$08: s := 'ftPioGrbit (Picture option flags)';
$09: s := 'ftPictFmla (Picture fmla-style macro)';
$0A: s := 'ftCbls (Checkbox link)';
$0B: s := 'ftRbo (Radio button)';
$0C: s := 'ftSbs (Scrollbar)';
$0D: s := 'ftNts (Note structure)';
$0E: s := 'ftSbsFmla (Scroll bar fmla-style macro)';
$0F: s := 'ftGboData (Group box data)';
$10: s := 'ftEdoData (Edit control data)';
$11: s := 'ftRboData (Radio button data)';
$12: s := 'ftCblsData (Check box data)';
$13: s := 'ftLbsData (List box data)';
$14: s := 'ftCblsFmla (Check box link fmla-style macro)';
$15: s := 'ftCmo (Common object data)';
else s := '(unknown)';
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [fieldType]),
Format('Subrecord type: %s', [s]));
inc(n);
Move(FBuffer[FBufferIndex], w, numBytes);
fieldSize := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(fieldSize), 'Size of subrecord');
inc(n);
case fieldType of
$000D:
begin // ftNts, from https://msdn.microsoft.com/en-us/library/office/dd951373%28v=office.12%29.aspx
numBytes := 16;
Move(FBuffer[FBufferIndex], guid, numBytes);
s := GuidToString(guid);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'GUID of comment');
inc(n);
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Shared Note (0 = false, 1 = true)');
inc(n);
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)),
'Unused (undefined, must be ignored)');
inc(n);
end;
$0015:
begin // common object data
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
case w of
$00: s := 'Group';
$01: s := 'Line';
$02: s := 'Rectangle';
$03: s := 'Oval';
$04: s := 'Arc';
$05: s := 'Chart';
$06: s := 'Text';
$07: s := 'Button';
$08: s := 'Picture';
$09: s := 'Polybon';
$0A: s := '(Reserved)';
$0B: s := 'Checkbox';
$0C: s := 'Option button';
$0D: s := 'Edit box';
$0E: s := 'Label';
$0F: s := 'Dialog box';
$10: s := 'Spinner';
$11: s := 'Scrollbar';
$12: s := 'List box';
$13: s := 'Group box';
$14: s := 'Combobox';
$15: s := '(Reserved)';
$16: s := '(Reserved)';
$17: s := '(Reserved)';
$18: s := '(Reserved)';
$19: s := 'Comment';
$1A: s := '(Reserved)';
$1B: s := '(Reserved)';
$1C: s := '(Reserved)';
$1D: s := '(Reserved)';
$1E: s := 'Microsoft Office drawing';
else s := '(Unknown)';
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [w]),
'Object type: '+s);
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.04x', [w]),
'Object ID number');
inc(n);
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Option flags:'#13);
if w and $0001 <> 0
then FDetails.Add('x Bit $0001 = 1: Object is locked when sheet is protected.')
else FDetails.Add(' Bit $0001 = 0: Object is NOT locked when sheet is protected.');
if w and $000E <> 0
then FDetails.Add('! Bit $0002 <> 0: Reserved - must be zero - THIS SEEMS TO BE AN ERROR!')
else FDetails.Add(' Bit $0002 = 0: Reserved - must be zero');
if w and $0010 <> 0
then FDetails.Add('x Bit $0010 = 1: Image of this object is intended to be included when printing')
else FDetails.Add(' Bit $0010 = 0: Image of this object is NOT intended to be included when printing');
if w and $1FE0 <> 0
then FDetails.Add('! Bits 12-5 <> 0: Reserved - must be zero - THIS SEEMS TO BE AN ERROR!')
else FDetails.Add(' Bits 12-5 = 0: Reserved - must be zero');
if w and $2000 <> 0
then FDetails.Add('x Bit $2000 = 1: Object uses automatic fill style.')
else FDetails.Add(' Bit $2000 = 0: Object does NOT use automatic fill style.');
if w and $4000 <> 0
then FDetails.Add('x Bit $4000 = 1: Object uses automatic line style.')
else FDetails.Add(' Bit $4000 = 0: Object does NOT use automatic line style.');
if w and $8000 <> 0
then FDetails.Add('! Bit $8000 = 1: Reserved - must be zero - THIS SEEMS TO BE AN ERROR!')
else FDetails.Add(' Bit $8000 = 0: Reserved - must be zero.');
end;
s := '';
if w and $0001 <> 0 then s := s + 'locked, ';
if w and $0010 <> 0 then s := s + 'print, ';
if w and $2000 <> 0 then s := s + 'automatic fill style, ';
if w and $4000 <> 0 then s := s + 'automatic line style, ';
if s <> '' then begin
Delete(s, Length(s)-1, 2);
s := 'Option flags:' + s;
end else
s := 'Option flags';
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [w]), s);
inc(n);
end;
end;
FBufferIndex := savedBufferIndex + 4 + fieldSize;
end;
RowCount := FixedRows + n;
end else
if FFormat = sfExcel5 then begin
// to do
end;
end;
procedure TBIFFGrid.ShowPageSetup;
var
numBytes: Integer;
w: Word;
s: String;
dbl: Double;
begin
if FFormat in [sfExcel5, sfExcel8] then RowCount := FixedRows + 11
else RowCount := FixedRows + 6;
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Paper size:'#13);
FDetails.Add(Format('%d - %s', [w, PaperSizeName(w)]));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Paper size');
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Scaling factor in percent');
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLETON(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Start page number');
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLETON(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'Fit worksheet width to this number of pages (0 = use as many as needed)');
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLETON(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'Fit worksheet height to this number of pages (0 = use as many as needed)');
{ Excel4 not supported in fpspreadsheet }
{
if FFormat = sfExcel4 then begin
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLETON(w);
if Row = FCurrRow then begin
FDetails.Add('Option flags:'#13);
if w and $0001 = 0
then FDetails.Add('Bit $0001 = 0: Print pages in columns')
else FDetails.Add('Bit $0001 = 1: Print pages in rows');
if w and $0002 = 0
then FDetails.add('Bit $0002 = 0: Landscape')
else FDetails.Add('Bit $0002 = 1: Portrait');
if w and $0004 = 0
then FDetails.Add('Bit $0004 = 0: Paper size, scaling factor, and paper orientation ' +
'(portrait/landscape) ARE initialised')
else FDetails.Add('Bit $0004 = 1: Paper size, scaling factor, and paper orientation ' +
'(portrait/landscape) are NOT initialised');
if w and $0008 = 0
then FDetails.Add('Bit $0008 = 0: Print colored')
else FDetails.add('Bit $0008 = 1: Print black and white');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x (%d)', [w, w]),
'Option flags');
end else }
if (FFormat in [sfExcel5, sfExcel8]) then begin
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Option flags:'#13);
if w and $0001 = 0
then FDetails.Add('Bit $0001 = 0: Print pages in columns')
else FDetails.Add('Bit $0001 = 1: Print pages in rows');
if w and $0002 = 0
then FDetails.add('Bit $0002 = 0: Landscape')
else FDetails.Add('Bit $0002 = 1: Portrait');
if w and $0004 = 0
then FDetails.Add('Bit $0004 = 0: Paper size, scaling factor, and paper orientation ' +
'(portrait/landscape) ARE initialised')
else FDetails.Add('Bit $0004 = 1: Paper size, scaling factor, and paper orientation ' +
'(portrait/landscape) are NOT initialised');
if w and $0008 = 0
then FDetails.Add('Bit $0008 = 0: Print colored')
else FDetails.add('Bit $0008 = 1: Print black and white');
if w and $0010 = 0
then FDetails.Add('Bit $0010 = 0: Default print quality')
else FDetails.Add('Bit $0010 = 1: Draft quality');
if w and $0020 = 0
then FDetails.Add('Bit $0020 = 0: Do NOT print cell notes')
else FDetails.Add('Bit $0020 = 0: Print cell notes');
if w and $0040 = 0
then FDetails.Add('Bit $0040 = 0: Use paper orientation (portrait/landscape) flag abov')
else FDetails.Add('Bit $0040 = 1: Use default paper orientation (landscape for chart sheets, portrait otherwise)');
if w and $0080 = 0
then FDetails.Add('Bit $0080 = 0: Automatic page numbers')
else FDetails.Add('Bit $0080 = 1: Use start page number above');
if w and $0200 = 0
then FDetails.Add('Bit $0200 = 0: Print notes as displayed')
else FDetails.Add('Bit $0200 = 1: Print notes at end of sheet');
case (w and $0C00) shr 10 of
0: FDetails.Add('Bit $0C00 = 0: Print errors as displayed');
1: FDetails.add('Bit $0C00 = 1: Do not print errors');
2: FDetails.Add('Bit $0C00 = 2: Print errors as "--"');
3: FDetails.Add('Bit $0C00 = 4: Print errors as "#N/A"');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x (%d)', [w, w]),
'Option flags');
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Print resolution in dpi');
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Vertical print resolution in dpi');
numBytes := 8;
Move(FBuffer[FBufferIndex], dbl, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl), 'Header margin');
Move(FBuffer[FBufferIndex], dbl, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl), 'Footer margin');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Number of copies to print');
end;
end;
procedure TBIFFGrid.ShowPalette;
var
numBytes: Integer;
w: Word;
dw: DWord;
npal: Integer;
i: Integer;
s: String;
begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
npal := WordLEToN(w);
RowCount := FixedRows + 1 + npal;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(npal),
'Number of palette colors');
for i := 0 to npal-1 do begin
numbytes := 4;
Move(FBuffer[FBufferIndex], dw, numBytes);
dw := DWordLEToN(dw);
s := Format('Palette color, index #%d ($%.2x)',[i, i]);
case i of
$00..$07: ;
$08..$3F: s := s + ', user-defined palette';
$40 : s := s + ', system window text color for border lines';
$41 : s := s + ', system window background color for pattern background';
$43 : s := s + ', system face color (dialogue background color)';
$4D : s := s + ', system window text colour for chart border lines';
$4E : s := s + ', system window background color for chart areas';
$4F : s := s + ', automatic color for chart border lines (seems to be always Black)';
$50 : s := s + ', system tool tip background color (used in note objects)';
$51 : s := s + ', system tool tip text color (used in note objects)';
$7FFF : s := s + ', system window text color for fonts';
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.8x', [dw]), s);
end;
end;
procedure TBIFFGrid.ShowPane;
var
numBytes: Integer;
w: Word;
b: Byte;
begin
RowCount := FixedRows + IfThen(FFormat < sfExcel5, 5, 6);
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Position of vertical split (twips or columns (if frozen))');
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(WordLEToN(w)),
'Position of horizontal split (twips or rows (if frozen))');
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index to first visible row in bottom pane(s)');
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index to first visible column in right pane(s)');
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(b),
'Identifier of pane with active cell cursor');
if FFormat >= sfExcel5 then begin
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(b), 'not used');
end;
end;
procedure TBIFFGrid.ShowPassword;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Password verifier for sheet or workbook:'#13);
if w = 0
then FDetails.Add('0 = No password')
else FDetails.Add(Format('$%.4x = Password verifier', [w]));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'Password verifier for sheet or workbook');
end;
procedure TBIFFGrid.ShowPrecision;
var
numbytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Precision-as-displayed mode:'#13);
if w = 0
then FDetails.Add('0 = Precision-as-displayed mode selected')
else FDetails.Add('1 = Precision-as-displayed mode NOT selected');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'Precision-as-displayed mode');
end;
procedure TBIFFGrid.ShowPrintGridLines;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Print sheet grid lines:'#13);
if w = 0
then FDetails.Add('0 = Do not print sheet grid lines')
else FDetails.Add('1 = Print sheet grid lines');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'Print sheet grid lines');
end;
procedure TBIFFGrid.ShowPrintHeaders;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Print row/column headers');
FDetails.Add('(the area with row numbers and column letters):'#13);
if w = 0
then FDetails.Add('0 = Do not print row/column headers')
else FDetails.Add('1 = Print row/column headers');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'Print row/column headers');
end;
procedure TBIFFGrid.ShowProt4Rev;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Removal of the shared workbook''s revision logs:'#13);
if w = 0
then FDetails.Add('0 = Removal of the shared workbook''s revision logs is allowed.')
else FDetails.Add('1 = Removal of the shared workbook''s revision logs is NOT allowed.');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'Removal of the shared workbook''s revision logs');
end;
procedure TBIFFGrid.ShowProt4RevPass;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Password verifier needed to change the PROT4REV record:'#13);
if w = 0
then FDetails.Add('0 = No password.')
else FDetails.Add(Format('$%.04x = Password verifier.', [w]));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w,w]),
'Password verifier needed to change the PROT4REV record');
end;
procedure TBIFFGrid.ShowProtect;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Protection state of the workbook:'#13);
if w = 0
then FDetails.Add(' 0 = Workbook is NOT protected.')
else FDetails.Add('x 1 = Workbook is protected.');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
'Protection state of the workbook');
end;
procedure TBIFFGrid.ShowRecalc;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('"Recalculate before save" option:'#13);
if w = 0
then FDetails.Add(' 0 = Do not recalculate')
else FDetails.Add('x 1 = Recalculate before saving the document');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'Recalculate before saving');
end;
procedure TBIFFGrid.ShowRefMode;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FbufferIndex], w, numbytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Cell reference mode:'#13);
if w = 0
then FDetails.Add('0 = RC mode (i.e. cell address shown as "R(1)C(-1)"')
else FDetails.Add('1 = A1 mode (i.e. cell address shown as "B1")');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
'Cell reference mode');
end;
procedure TBIFFGrid.ShowRefreshAll;
var
numbytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('RefreshAll record:'#13);
if w = 0
then FDetails.Add(' 0 = Do not force refresh of external data ranges, PivotTables and XML maps on workbook load.')
else FDetails.Add('x 1 = Force refresh of external data ranges, PivotTables and XML maps on workbook load.');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w, w]),
'Force refresh of external data ranges, Pivot tables and XML maps on workbook load');
end;
procedure TBIFFGrid.ShowRightMargin;
var
numBytes: Integer;
dbl: Double;
begin
RowCount := FixedRows + 1;
numBytes := 8;
Move(FBuffer[FBufferIndex], dbl, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl),
'Right page margin in inches (IEEE 754 floating-point value, 64-bit double precision)');
end;
procedure TBIFFGrid.ShowRK;
var
numBytes: Integer;
w: Word;
dw: DWord;
encint: DWord;
encdbl: QWord;
dbl: Double absolute encdbl;
s: String;
begin
RowCount := FixedRows + 4;
ShowRowColData(FBufferIndex);
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index to XF record');
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numBytes);
dw := DWordLEToN(dw);
if dw and $00000002 = 0 then begin
encdbl := (QWord(dw) and QWord($FFFFFFFFFFFFFFFC)) shl 32;
if dw and $00000001 = 1 then
s := Format('%.2f', [dbl*0.01])
else
s := Format('%.0f', [dbl]);
end
else begin
s := Format('$%.16x', [-59000000]);
encint := ((dw and DWord($FFFFFFFC)) shr 2) or (dw and DWord($C0000000));
// "arithmetic shift" = replace left-most bits by original bits
if dw and $00000001 = 1 then
s := FloatToStr(encint*0.01)
else
s := IntToStr(encint);
end;
if Row = FCurrRow then begin
FDetails.Add('RK Value:'#13);
if dw and $00000001 = 0
then FDetails.Add('Bit 0 = 0: Value not changed')
else FDetails.Add('Bit 0 = 1: Encoded value is multiplied by 100.');
if dw and $00000002 = 0
then FDetails.Add('Bit 1 = 0: Floating point value')
else FDetails.Add('Bit 1 = 1: Signed integer value');
FDetails.Add('Bits 31-2: Encoded value ' + s);
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes,
Format('$%.8x', [QWord(dw)]), 'RK value ['+s+']');
end;
procedure TBIFFGrid.ShowRow;
var
numBytes: Integer;
dw: DWord;
w: Word;
b: Byte;
begin
RowCount := FixedRows + IfThen(FFormat = sfExcel2, 10, 7);
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index of this row');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index to column of the first cell which is described by a cell record');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index to column of the last cell which is described by a cell record, increased by 1');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Row height:'#13);
FDetails.Add(Format('Bits 14-0 = %d: Row height in twips (1/20 pt) --> %.1f-pt',
[w and $7FFF, (w and $7FFF)/20.0])
);
if w and $8000 = 0
then FDetails.Add('Bit 15 = 0: Row has custom height')
else FDetails.Add('Bit 15 = 1: Row has default height');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
'Bits 14-0: Height of row in twips (1/20 pt), Bit 15: Row has default height?');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, '', '(not used)');
if FFormat = sfExcel2 then begin
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
'0=No defaults written, 1=Default row attribute field and XF index occur below');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Relative offset to calculate stream position of the first cell record for this row');
if b = 1 then begin
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
'Cell protection and XF index');
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
'Indexes to FORMAT and FONT records');
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
'Cell style');
end;
end
else begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, '',
'In BIFF5-BIFF8 this field is not used anymore, but the DBCELL record instead.');
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
dw := DWordLEToN(dw);
if Row = FCurrRow then begin
FDetails.Add('Option flags and default row formatting:'#13);
FDetails.Add(Format('Bits 0-2 = %d: Outline level of the row', [dw and $00000007]));
if dw and $00000010 = 0
then FDetails.Add('Bit 4 = 0: Outline group does not start or end here and is not collapsed')
else FDetails.Add('Bit 4 = 1: Outline group starts or ends here and is collapsed');
if dw and $00000020 = 0
then FDetails.Add('Bit 5 = 0: Row is NOT hidden')
else FDetails.Add('Bit 5 = 1: Row is hidden');
if dw and $00000040 = 0
then FDetails.Add('Bit 6 = 0: Row height and default font height do match.')
else FDetails.Add('Bit 6 = 1: Row height and default font height do NOT match.');
if dw and $00000080 = 0
then FDetails.Add('Bit 7 = 0: Row does NOT have explicit default format.')
else FDetails.Add('Bit 7 = 1: Row has explicit default format.');
FDetails.Add('Bit 8 = 1: Is always 1');
FDetails.Add(Format('Bits 16-27 = %d: Index to default XF record', [(dw and $0FFF0000) shr 16]));
if dw and $10000000 = 0
then FDetails.Add('Bit 28 = 0: No additional space above the row.')
else FDetails.Add('Bit 28 = 1: Additional space above the row.');
if dw and $20000000 = 0
then FDetails.Add('Bit 29 = 0: No additional space below the row.')
else FDetails.Add('Bit 29 = 1: Additional space below the row.');
if dw and $40000000 = 0
then FDetails.Add('Bit 30 = 0: D0 NOT show phonetic text for all cells in the row.')
else FDetails.Add('Bit 30 = 1: Show phonetic text for all cells in the row.');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.8x', [dw]),
'Option flags and default row formatting');
end;
end;
procedure TBIFFGrid.ShowRowColData(var ABufIndex: LongWord);
var
w: Word;
numBytes: Integer;
begin
// Row
numBytes := 2;
Move(FBuffer[ABufIndex], w, numBytes);
ShowInRow(FCurrRow, ABufIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to row');
// Column
numBytes := 2;
Move(FBuffer[ABufIndex], w, numBytes);
ShowInRow(FCurrRow, ABufIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to column');
end;
procedure TBIFFGrid.ShowRString;
var
numBytes: Integer;
b: Byte;
w: Word;
s: String;
len: Integer;
j: Integer;
wideStr: wideString;
ansiStr: ansiString;
begin
if FFormat < sfExcel5 then
exit;
RowCount := FixedRows + 5;
ShowRowColData(FBufferIndex);
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
'Index of XF record');
// String length
Move(FBuffer[FBufferIndex], w, 2);
len := WordLEToN(w);
if FFormat = sfExcel8 then
begin
SetLength(widestr, len);
Move(FBuffer[FBufferIndex+3], widestr[1], len*2);
s := UTF8Encode(WideStringLEToN(widestr));
numbytes := 3 + len*2;
end else
begin
SetLength(ansistr, len);
Move(FBuffer[FBufferIndex+2], ansistr[1], len);
s := CP1252ToUTF8(ansistr);
numbytes := 2 + len;
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
Format('%s string, 16-bit string length', [GetStringType]));
// Number of rich-text formatting runs
numbytes := IfThen(FFormat = sfExcel8, 2, 1);
Move(FBuffer[FBufferIndex], w, numbytes);
len := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(len),
'Count of rich-text formatting runs');
// Formatting run data
RowCount := RowCount + 2*len;
for j:=0 to len-1 do
begin
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
Format('Rich-Text formatting run #%d, index of first character', [j]));
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
Format('Rich-Text formatting run #%d, font index', [j]));
end;
end;
procedure TBIFFGrid.ShowSelection;
var
numBytes: Integer;
w: word;
b: Byte;
i, n: Integer;
begin
Move(FBuffer[FBufferIndex+7], w, 2);
n := WordLEToN(w);
RowCount := FixedRows + 5 + n*4;
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Pane identifier (see PANE record)');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index to row of the active cell');
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index to column of the active cell');
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index into the following cell range list to the entry that contains the active cell');
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(n),
'Number of following cell range addresses');
numbytes := 2;
for i:=1 to n do begin
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to first row');
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to last row');
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Index to first column');
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Index to last column');
end;
end;
procedure TBIFFGrid.ShowSharedFormula;
var
numBytes: Integer;
b: Byte;
w: Word;
tokenBytes: Integer;
begin
BeginUpdate;
RowCount := FixedRows + 1000;
// Brute force simplification because of unknown row count at this point
// Will be reduced at the end.
// Index to first row
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to first row');
// Index to last row
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to last row');
// Index to first column
numBytes := 1; // 8-bit also for BIFF8!
Move(FBuffer[FBufferIndex], b, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Index to first column');
// Index to last column
numBytes := 1; // 8-bit also for BIFF8!
Move(FBuffer[FBufferIndex], b, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Index to last column');
// Not used
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Not used');
// Number of existing FORMULA records for this shared formula
numBytes := 1;
Move(FBuffer[FBufferIndex], b, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Number of FORMULA records in shared formula');
// Size of Token array (in Bytes)
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
tokenBytes := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(tokenBytes),
'Size of formula data (in Bytes)');
// Formula tokens
ShowFormulaTokens(tokenBytes);
RowCount := FCurrRow;
EndUpdate(true);
end;
procedure TBIFFGrid.ShowSheet;
var
numBytes: Integer;
dw: DWord;
b: Byte;
s: String;
begin
RowCount := FixedRows + 4;
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numBytes);
dw := DWordLEToN(dw);
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.8x)', [dw, dw]),
'Absolute stream position of BOF record of sheet represented by this record.');
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
'Sheet state (0=visible, 1=hidden, 2="very" hidden)');
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]),
'Sheet type ($00=worksheet, $02=Chart, $06=VB module)');
ExtractString(FBufferIndex, 1, (FFormat = sfExcel8), s, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, IfThen(FFormat=sfExcel8,
'Sheet name (unicode string, 8-bit string length)',
'Sheet name (byte string, 8-bit string length)')
);
end;
procedure TBIFFGrid.ShowSheetPR;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Option flags:'#13);
if w and $0001 = 0
then FDetails.Add(' Bit $0001 = 0: Do not show automatic page breaks')
else FDetails.Add('x Bit $0001 = 1: Show automatic page breaks');
if w and $0010 = 0
then FDetails.Add(' Bit $0010 = 0: Standard sheet')
else FDetails.Add('x Bit $0010 = 1: Dialog sheet (BIFF5-BIFF8)');
if w and $0020 = 0
then FDetails.Add(' Bit $0020 = 0: No automatic styles in outlines')
else FDetails.Add('x Bit $0020 = 1: Apply automatic styles to outlines');
if w and $0040 = 0
then FDetails.Add(' Bit $0040 = 0: Outline buttons above outline group')
else FDetails.Add('x Bit $0040 = 1: Outline buttons below outline group');
if w and $0080 = 0
then FDetails.Add(' Bit $0080 = 0: Outline buttons left of outline group')
else FDetails.Add('x Bit $0080 = 1: Outline buttons right of outline group');
if w and $0100 = 0
then FDetails.Add(' Bit $0100 = 0: Scale printout in percent')
else FDetails.Add('x Bit $0100 = 1: Fit printout to number of pages');
if w and $0200 = 0
then FDetails.Add(' Bit $0200 = 0: Save external linked values (BIFF3-BIFF4 only)')
else FDetails.Add('x Bit $0200 = 1: Do NOT save external linked values (BIFF3-BIFF4 only)');
if w and $0400 = 0
then FDetails.Add(' Bit $0400 = 0: Do not show row outline symbols')
else FDetails.Add('x Bit $0400 = 1: Show row outline symbols');
if w and $0800 = 0
then FDetails.Add(' Bit $0800 = 0: Do not show column outline symbols')
else FDetails.Add('x Bit $0800 = 1: Show column outline symbols');
case (w and $3000) shr 12 of
0: FDetails.Add('x Bits $3000 = $0000: Arrange windows tiled');
1: FDetails.Add('x Bits $3000 = $1000: Arrange windows horizontal');
2: FDetails.Add('x Bits $3000 = $2000: Arrange windows vertical');
3: FDetails.Add('x Bits $3000 = $3000: Arrange windows cascaded');
end;
if w and $4000 = 0
then FDetails.Add('x Bits $4000 = 0: Excel like expression evaluation (BIFF4-BIFF8 only)')
else FDetails.Add('x Bits $4000 = 1: Lotus like expression evaluation (BIFF4-BIFF8 only)');
if w and $8000 = 0
then FDetails.Add('x Bits $8000 = 0: Excel like formula editing (BIFF4-BIFF8 only)')
else FDetails.Add('x Bits $8000 = 1: Lotus like formula editing (BIFF4-BIFF8 only)');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x (%d)', [w, w]),
'Option flags');
end;
procedure TBIFFGrid.ShowSST;
var
numBytes: Integer;
s: String;
total1, total2: DWord;
i, j, n: Integer;
rtfRuns: TRichTextFormattingRuns;
rtfIndex: LongWord;
w: Word;
begin
numBytes := 4;
Move(FBuffer[FBufferIndex], total1, numBytes);
Move(FBuffer[FBufferIndex+4], total2, numBytes);
total1 := DWordLEToN(total1);
total2 := DWordLEToN(total2);
FTotalSST := total2;
RowCount := FixedRows + 1000;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(total1),
'Total number of shared strings in the workbook');
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(total2),
'Number of following strings');
FPendingCharCount := -1;
n := 0;
for i:=1 to FTotalSST do begin
FCounterSST := i;
ExtractString(FBufferIndex, 2, true, s, numBytes, rtfRuns, rtfIndex); // BIFF8 only --> 2 length bytes
inc(n);
if FPendingCharCount = 0 then begin
ShowInRow(FCurrRow, FBufferIndex, numbytes, s, IfThen(Length(rtfRuns) > 0,
Format('Shared string #%d (Count of Rich-Text formatting runs: %d)', [i, Length(rtfRuns)]),
Format('Shared string #%d', [i])));
// ShowInRow(FCurrRow, FBufferIndex, numBytes, s, Format('Shared string #%d', [i]));
for j:=0 to High(rtfRuns) do
begin
ShowInRow(FCurrRow, rtfIndex, 2, IntToStr(rtfRuns[j].FirstIndex),
Format(' Rich-Text formatting run #%d, index of first character', [j]));
ShowInRow(FCurrRow, rtfIndex, 2, IntToStr(rtfRuns[j].FontIndex),
Format(' Rich-Text formatting run #%d, font index', [j]));
inc(n, 2);
end;
end
else
begin
ShowInRow(FCurrRow, FBufferIndex, numbytes, s, Format('Shared string #%d - partial (--> CONTINUE)', [i]));
FInfo := BIFFNODE_SST_CONTINUE;
break;
end;
end;
RowCount := FixedRows + 2 + n;
end;
procedure TBIFFGrid.ShowStandardWidth;
var
w: Word;
numBytes: Integer;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d (%f characters)', [w, w/256]),
'Default column width (overrides DFCOLWIDTH, in 1/256 of "0" width)');
end;
procedure TBIFFGrid.ShowString;
var
numBytes: Integer;
s: String;
begin
RowCount := FixedRows + 1;
case FFormat of
sfExcel2:
begin
ExtractString(FBufferIndex, 1, false, s, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Byte string, 8-bit string length');
end;
sfExcel5:
begin
ExtractString(FBufferIndex, 2, false, s, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Byte string, 16-bit string length');
end;
sfExcel8:
begin
ExtractString(FBufferIndex, 2, true, s, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Unicode string, 16-bit string length');
end;
end;
end;
procedure TBIFFGrid.ShowStyle;
var
numBytes: Integer;
b: Byte;
w: Word;
s: String;
isRowLevel: Boolean;
isColLevel: Boolean;
begin
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
if w and $8000 = 0 then
RowCount := FixedRows + 2
else
RowCount := FixedRows + 3;
if Row = FCurrRow then begin
FDetails.Add('Style:'#13);
FDetails.Add(Format('Bits 0-11 = %d: Index to style XF record', [w and $0FFFF]));
if w and $8000 = 0
then FDetails.Add('Bit 15 = 0: user-defined style')
else FDetails.Add('Bit 15 = 1: built-in style');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), 'Style index and type');
if w and $8000 = 0 then begin
if FFormat = sfExcel8 then begin
ExtractString(FBufferIndex, 2, true, s, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Style name (Unicode string, 16-bit string length)');
end else begin
ExtractString(FBufferIndex, 1, false, s, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Style name (Byte string, 8-bit string length)');
end;
end else begin
numbytes := 1;
b := FBuffer[FBufferIndex];
isRowLevel := (b = 1);
isColLevel := (b = 2);
if FCurrRow = Row then begin
FDetails.Add('Identifier for built-in cell style:'#13);
case b of
0: FDetails.Add('0 = normal');
1: FDetails.Add('1 = RowLevel (see next field)');
2: FDetails.Add('2 = ColLevel (see next field)');
3: FDetails.Add('3 = Comma');
4: FDetails.Add('4 = Currency');
5: FDetails.Add('5 = Percent');
6: FDetails.Add('6 = Comma [0]');
7: FDetails.Add('7 = Currency [0]');
8: FDetails.Add('8 = Hyperlink');
9: FDetails.Add('9 = Followed hyperlink');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
'Identifier for built-in cell style');
b := FBuffer[FBufferIndex];
if FCurrRow = Row then begin
FDetails.Add('Level for RowLevel or ColLevel style (zero-based):'#13);
if b = $FF then
FDetails.Add('$FF = no RowLevel or ColLevel style')
else
if isRowLevel then
FDetails.Add('RowLevel = ' + IntToStr(b))
else if isColLevel then
FDetails.Add('ColLevel = ' + IntToStr(b));
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
'Level for RowLevel or ColLevel style (if available)');
end;
end;
procedure TBIFFGrid.ShowStyleExt;
var
numBytes: Integer;
w: Word;
b: Byte;
bs: Byte;
s: String;
begin
RowCount := FixedRows + 11;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [wordLEToN(w)]),
'Future record type');
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Attributes:'#13);
if w and $0001 = 0
then FDetails.Add(' Bit 0 = 0: The containing record does not specify a range of cells.')
else FDetails.Add('x Bit 0 = 1: The containing record specifies a range of cells.');
FDetails.Add('Bit 1: specifies wether to alert the user of possible problems '+
'when saving the file whithout having reckognized this record.');
FDetails.Add('Bits 2-15: reserved (MUST be zero, MUST be ignored)');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]),
'Attributes');
numbytes := 8;
ShowInRow(FCurrRow, FBufferIndex, numbytes, '', 'Reserved');
numbytes := 1;
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Flags:'#13);
if b and $01 = 0
then FDetails.Add(' Bit 0 = 0: no built-in style')
else FDetails.Add('x Bit 0 = 1: built-in style');
if b and $02 = 0
then FDetails.Add(' Bit 1 = 0: NOT hidden')
else FDetails.Add('x Bit 1 = 1: hidden (i.e. is displayed in user interface)');
FDetails.Add('Bit 2: specifies whether the built-in cell style was modified '+
'by the user and thus has a custom definition.');
FDetails.Add('Bit 3-7: Reserved');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), 'Flags');
numbytes := 1;
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Category:'#13);
case b of
0: FDetails.Add('Bits 0-7 = 0: Custom style');
1: FDetails.Add('Bits 0-7 = 1: Good, bad, neutral style');
2: FDetails.Add('Bits 0-7 = 2: Data model style');
3: FDetails.Add('Bits 0-7 = 3: Title and heading style');
4: FDetails.Add('Bits 0-7 = 4: Themed cell style');
5: FDetails.Add('Bits 0-7 = 5: Number format style');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), 'Category');
numbytes := 1;
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Built-in style:'#13);
FDetails.Add('An unsigned integer that specifies the type of the built-in cell style.');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), 'Built-in style');
bs := b;
numbytes := 1;
b := FBuffer[FBufferindex];
if Row = FCurrRow then begin
FDetails.Add('Outline depth level:'#13);
FDetails.Add('An unsigned integer that specifies the depth level of row/column automatic outlining.');
if (bs in [1, 2]) then
FDetails.Add(Format('Bits 0-7 = %d: Outline level is %d', [b, b+1]))
else
FDetails.Add(Format('Bits 0-7 = $%.2x: MUST be $FF, MUST be ignoried', [b]));
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [b]), 'Outline depth level');
ExtractString(FBufferIndex, 1, true, s, numBytes, true);
ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Name of the style name to extend (Unicode string, 8-bit string length)');
numbytes := 2;
ShowInRow(FCurrRow, FBufferIndex, numbytes, '', 'XFProps (reserved)');
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Count of XFProp structures to follow in array');
end;
procedure TBiffGrid.ShowTabID;
var
numbytes: Integer;
w: word;
i, n: Integer;
begin
numbytes := 2;
n := Length(FBuffer) div numbytes;
RowCount := FixedRows + n;
for i := 1 to n do begin
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w, w]),
'Unique sheet identifier');
end;
end;
procedure TBIFFGrid.ShowTopMargin;
var
numBytes: Integer;
dbl: Double;
begin
RowCount := FixedRows + 1;
numBytes := 8;
Move(FBuffer[FBufferIndex], dbl, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl),
'Top page margin in inches (IEEE 754 floating-point value, 64-bit double precision)');
end;
procedure TBIFFGrid.ShowTXO;
var
numbytes: Word;
w: Word;
s, sh, sv, sl: String;
begin
RowCount := FixedRows + 9;
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
sh := '';
case (w and $000E) shr 1 of
1: sh := sh + 'left-aligned, ';
2: sh := sh + 'centered, ';
3: sh := sh + 'right-aligned, ';
4: sh := sh + 'justified, ';
end;
sv := '';
case (w and $0070) shr 4 of
1: sv := sv + 'top, ';
2: sv := sv + 'middle, ';
3: sv := sv + 'bottom, ';
4: sv := sv + 'justify, ';
end;
s := sh + sv;
if (w and $0200) shr 9 <> 0 then s := s + 'lock text, ';
if Row = FCurrRow then begin
FDetails.Add( 'Option flags:'#13);
FDetails.Add( 'Bit 0: Reserved');
if sh = '' then sh := 'none';
FDetails.Add( 'Bits 1-3: 0 = Horizontal text alignment: ' + sh);
if sv = '' then sv := 'none';
FDetails.Add( 'Bits 4-6: 0 = Vertical text alignment: ' + sv);
FDetails.Add( 'Bits 7-8: Reserved');
case (w and $0200) shr 9 of
0: FDetails.Add('Bit 9: Lock Text Option is off.');
1: FDetails.Add('Bit 9: Lock Text Option is on');
end;
FDetails.Add( 'Bits 10-15: Reserved');
end;
if s <> '' then begin
Delete(s, Length(s)-1, 2);
s := 'Option flags: ' + s;
end else
s := 'Option flags';
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), s);
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
case w of
0: s := 'No rotation (text appears left to right';
1: s := 'Text appears top to bottom; letters are upright';
2: s := 'Text is rotated 90 degrees counterclockwise';
3: s := 'Text is rotated 90 degrees clockwise';
end;
if Row = FCurrRow then begin
FDetails.Add('Orientation of text with the object boundary:'#13);
FDetails.Add(Format('%d = %s', [w, s]));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
'Orientation of text within the object boundary: ' + s);
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
'Reserved (must be 0)');
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
'Reserved (must be 0)');
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
'Reserved (must be 0)');
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
'Length (in characters) of text (in first following CONTINUE record)');
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
'Length of formatting runs (in second following CONTINUE record)');
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
'Reserved (must be 0)');
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
'Reserved (must be 0)');
end;
procedure TBIFFGrid.ShowWindow1;
var
numBytes: Word;
b: Byte;
w: word;
begin
RowCount := FixedRows + IfThen(FFormat < sfExcel5, 5, 9);
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Horizontal position of the document window (in twips = 1/20 pt)');
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Vertical position of the document window (in twips = 1/20 pt)');
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Width of the document window (in twips = 1/20 pt)');
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Height of the document window (in twips = 1/20 pt)');
if FFormat < sfExcel5 then begin
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(b)),
'0 = Window is visible; 1 = window is hidden');
end else begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Option flags:');
if w and $0001 = 0
then FDetails.Add(' Bit $0001 = 0: Window is visible')
else FDetails.Add('x Bit $0001 = 1: Window is hidden');
if w and $0002 = 0
then FDetails.Add(' Bit $0002 = 0: Window is open')
else FDetails.Add('x Bit $0002 = 1: Window is minimized');
if w and $0008 = 0
then FDetails.Add(' Bit $0008 = 0: Horizontal scrollbar hidden')
else FDetails.Add('x Bit $0008 = 1: Horizontal scrollbar visible');
if w and $0010 = 0
then FDetails.Add(' Bit $0010 = 0: Vertical scrollbar hidden')
else FDetails.Add('x Bit $0010 = 1: Vertical scrollbar visible');
if w and $0020 = 0
then FDetails.Add(' Bit $0020 = 0: Worksheet tab bar hidden')
else FDetails.Add('x Bit $0020 = 1: Worksheet tab bar visible');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w, w]),
'Option flags');
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index to active (displayed) worksheet');
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index of first visible tab in the worksheet tab bar');
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Number of selected worksheets (highlighted in the worksheet tab bar)');
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Width of worksheet tab bar (in 1/1000 of window width). '+
'The remaining space is used by the horizontal scrollbar.');
end;
end;
procedure TBIFFGrid.ShowWindow2;
var
numBytes: Word;
b: Byte;
w: word;
dw : DWord;
begin
if FFormat = sfExcel2 then begin
RowCount := FixedRows + 9;
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
'0 = Show formula results; 1 = Show formulas');
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
'0 = Do not show grid lines; 1 = Show grid lines');
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
'0 = Do not show sheet headers; 1 = Show sheet headers');
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
'0 = Panes are not frozen; 1 = Panes are frozen');
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
'0 = Show zero values as empty cells; 1 = Show zero values');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index to first visible row');
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index to first visible column');
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
'0 = Use manual grid line colour (below); 1 = Use automatic grid line colour');
numbytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [DWordLEToN(dw)]),
'Grid line RGB color');
end else begin
RowCount := FixedRows + IfThen(FFormat = sfExcel5, 4, 8);
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Option flags:');
if w and $0001 = 0
then FDetails.Add(' Bit $0001 = 0: Show formula results')
else FDetails.Add('x Bit $0001 = 1: Show formulas');
if w and $0002 = 0
then FDetails.Add(' Bit $0002 = 0: Do not show grid lines')
else FDetails.Add('x Bit $0002 = 1: Show grid lines');
if w and $0004 = 0
then FDetails.Add(' Bit $0004 = 0: Do not show sheet headers')
else FDetails.Add('x Bit $0004 = 1: Show sheet headers');
if w and $0008 = 0
then FDetails.Add(' Bit $0008 = 0: Panes are not frozen')
else FDetails.Add('x Bit $0008 = 1: Panes are frozen');
if w and $0010 = 0
then FDetails.Add(' Bit $0010 = 0: Show zero values as empty cells')
else FDetails.Add('x Bit $0010 = 1: Show zero values');
if w and $0020 = 0
then FDetails.Add(' Bit $0020 = 0: Manual grid line color')
else FDetails.Add('x Bit $0020 = 1: Automatic grid line color');
if w and $0040 = 0
then FDetails.Add(' Bit $0040 = 0: Columns from left to right')
else FDetails.Add('x Bit $0040 = 1: Columns from right to left');
if w and $0080 = 0
then FDetails.Add(' Bit $0080 = 0: Do not show outline symbols')
else FDetails.Add('x Bit $0080 = 1: Show outline symbols');
if w and $0100 = 0
then FDetails.Add(' Bit $0100 = 0: Keep splits if pane freeze is removed')
else FDetails.Add('x Bit $0100 = 1: Remove splits if pane freeze is removed');
if w and $0200 = 0
then FDetails.Add(' Bit $0200 = 0: Sheet not selected')
else FDetails.Add('x Bit $0200 = 1: Sheet selected');
if w and $0400 = 0
then FDetails.Add(' Bit $0400 = 0: Sheet not active')
else FDetails.Add('x Bit $0400 = 1: Sheet active');
if w and $0800 = 0
then FDetails.Add(' Bit $0800 = 0: Show in normal view')
else FDetails.Add('x Bit $0800 = 1: Show in page break preview');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w, w]),
'Option flags');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index to first visible row');
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Index to first visible column');
if FFormat =sfExcel5 then begin
numbytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [DWordLEToN(dw)]),
'Grid line RGB color');
end else begin
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Color index of grid line color');
ShowInRow(FCurrRow, FBufferIndex, numbytes, '', 'Not used');
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Cached magnification factor in page break preview (in percent); 0 = Default (60%)');
Move(FBuffer[FBufferIndex], w, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
'Cached magnification factor in normal view (in percent); 0 = Default (100%)');
numBytes := 4;
ShowInRow(FCurrRow, FBufferIndex, numbytes, '', 'Not used');
end;
end;
end;
procedure TBIFFGrid.ShowWindowProtect;
var
numBytes: Integer;
w: Word;
begin
RowCount := FixedRows + 1;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Protection state of workbook windows:'#13);
if w = 0
then FDetails.Add('0 = The workbook windows can be resized or moved and the window state can be changed.')
else FDetails.Add('1 = The workbook windows cannot be resized or moved and the window state cannot be changed.');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
'Protection state of the workbook windows');
end;
procedure TBIFFGrid.ShowWriteAccess;
var
numbytes: Integer;
s: String;
begin
RowCount := FixedRows + 1;
ExtractString(FBufferIndex, IfThen(FFormat=sfExcel8, 2, 1), FFormat=sfExcel8, s, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'User name (i.e., the name that you type when you install Microsoft Excel');
end;
procedure TBIFFGrid.ShowWriteProt;
begin
RowCount := FixedRows + 2;
ShowInRow(FCurrRow, FBufferIndex, 0, '', 'Write protect: if present file is write-protected');
ShowInRow(FCurrRow, FBufferIndex, 0, '', 'Write protection password is in FILESHARING record');
end;
procedure TBIFFGrid.ShowXF;
var
numBytes: Word;
b: Byte;
w: word;
dw : DWord;
begin
if FFormat = sfExcel2 then begin
RowCount := FixedRows + 4;
numBytes := 1;
b := FBuffer[FBufferIndex];
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
'Index to font record');
ShowInRow(FCurrRow, FBufferIndex, numBytes, '',
'(not used)');
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Number format and cell flags:'#13);
FDetails.Add(Format('Bits 0-5 = %d: Index to FORMAT record', [b and $3F]));
if b and $40 = 0
then FDetails.Add('Bit 6 = 0: Cell is not locked')
else FDetails.Add('Bit 6 = 1: Cell is locked');
if b and $80 = 0
then FDetails.Add('Bit 7 = 0: Formula is not hidden')
else FDetails.Add('Bit 7 = 1: Formula is hidden');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b, b]),
'Number format and cell flags');
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Horizontal alignment, border style and background:'#13);
case b and $07 of
0: FDetails.Add('Bits $07 = 0: Horizontal alignment "General"');
1: FDetails.Add('Bits $07 = 1: Horizontal alignment "Left"');
2: FDetails.Add('Bits $07 = 2: Horizontal alignemnt "Centered"');
3: FDetails.Add('Bits $07 = 3: Horizontal alignment "Right"');
4: FDetails.Add('Bits $07 = 4: Horizontal alignment "Filled"');
5: FDetails.Add('Bits $07 = 5: Horizontal alignment "Justified"');
6: FDetails.Add('Bits $07 = 6: Horizontal alignment "Centred across selection"');
7: FDetails.Add('Bits $07 = 7: Horizontal alignment "Distributed"');
end;
if b and $08 = 0
then FDetails.Add('Bit $08 = 0: Cell has no left border')
else FDetails.Add('Bit $08 = 1: Cell has left black border');
if b and $10 = 0
then FDetails.Add('Bit $10 = 0: Cell has no right border')
else FDetails.Add('Bit $10 = 1: Cell has right black border');
if b and $20 = 0
then FDetails.Add('Bit $20 = 0: Cell has no top border')
else FDetails.Add('Bit $20 = 1: Cell has top black border');
if b and $40 = 0
then FDetails.Add('Bit $40 = 0: Cell has no bottom border')
else FDetails.Add('Bit $40 = 1: Cell has bottom black border');
if b and $80 = 0
then FDetails.Add('Bit $80 = 0: Cell has no shaded background')
else FDetails.Add('Bit $80 = 1: Cell has shaded background');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]),
'Horizontal alignment, border style, and background');
end
else
begin // XF (BIFF5 and BIFF8)
RowCount := FixedRows + IfThen(FFormat=sfExcel5, 7, 10);
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index to font record');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
'Index to format record');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('XFType, cell protection, parent style XF:'#13);
if w and $0001 = 0
then FDetails.Add('Bit $0001 = 0: Cell is not locked')
else FDetails.Add('Bit $0001 = 1: Cell is locked');
if w and $0002 = 0
then FDetails.Add('Bit $0002 = 0: Formula is not hidden')
else FDetails.Add('Bit $0002 = 1: Formula is hidden');
if w and $0004 = 0
then FDetails.Add('Bit $0004 = 0: Cell XF')
else FDetails.Add('Bit $0004 = 1: Style XF');
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
'XFType, cell protection, parent style XF');
numBytes := 1;
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Alignment and text break:'#13);
case b and $03 of
0: FDetails.Add('Bits 0-2 = 0: Horizontal alignment "General"');
1: FDetails.Add('Bits 0-2 = 1: Horizontal alignment "Left"');
2: FDetails.Add('Bits 0-2 = 2: Horizontal alignemnt "Centered"');
3: FDetails.Add('Bits 0-2 = 3: Horizontal alignment "Right"');
4: FDetails.Add('Bits 0-2 = 4: Horizontal alignment "Filled"');
5: FDetails.Add('Bits 0-2 = 5: Horizontal alignment "Justified"');
6: FDetails.Add('Bits 0-2 = 6: Horizontal alignment "Centred across selection"');
7: if FFormat = sfExcel8 then
FDetails.Add('x Bits 0-2 = 7: Horizontal alignment "Distributed"');
end;
if b and $08 = 0
then FDetails.Add('Bit 3 = 0: Text is not wrapped.')
else FDetails.Add('Bit 3 = 1: Text is wrapped at right border.');
case (b and $70) shr 4 of
0: FDetails.Add('Bits 4-6 = 0: Vertical alignment "Top"');
1: FDetails.Add('Bits 4-6 = 1: Vertical alignment "Centered"');
2: FDetails.Add('Bits 4-6 = 2: Vertical alignment "Bottom"');
3: FDetails.Add('Bits 4-6 = 3: Vertical alignment "Justified"');
4: if FFormat = sfExcel8 then
FDetails.Add('Bits 4-6 = 4: Vertical alignment "Distributed"');
end;
if FFormat = sfExcel8 then begin
if b and $80 = 0
then FDetails.Add('Bit 3 = 0: Do NOT justify last line in justified or distibuted text')
else FDetails.Add('Bit 3 = 1: Justify last line in justified or distibuted text');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]),
'Alignment and text break');
numBytes := 1;
b := FBuffer[FBufferIndex];
if FFormat = sfExcel5 then begin
if Row = FCurrRow then begin
FDetails.Add('Text orientation and flags for used attribute groups:'#13);
case (b and $03) of
0: FDetails.Add('Bits $03 = 0: not rotated');
1: FDetails.Add('Bits $03 = 1: not rotated, letters stacked top-to-bottom');
2: FDetails.Add('Bits $03 = 2: text rotated 90° counter-clockwise');
3: FDetails.Add('Bits $03 = 3: text rotated 90° clockwise');
end;
if b and $04 = 0
then FDetails.Add('Bit $04 = 0: No flag for number format')
else FDetails.Add('Bit $04 = 1: Flag for number format');
if b and $08 = 0
then FDetails.Add('Bit $08 = 0: No flag for font')
else FDetails.Add('Bit $08 = 2: Flag for font');
if b and $10 = 0
then FDetails.Add('Bit $10 = 0: No flag for hor/vert alignment, text wrap, indentation, orientation, rotation, and text direction')
else FDetails.Add('Bit $10 = 1: Flag for hor/vert alignment, text wrap, indentation, orientation, rotation, and text direction');
if b and $20 = 0
then FDetails.Add('Bit $20 = 0: No flag for border lines')
else FDetails.Add('Bit $20 = 1: Flag for border lines');
if b and $40 = 0
then FDetails.Add('Bit $40 = 0: No flag for background area style')
else FDetails.Add('Bit $40 = 1: Flag for background area style');
if b and $80 = 0
then FDetails.Add('Bit $80 = 0: No flag for cell protection (cell locked and formula hidden)')
else FDetails.Add('Bit $80 = 1: Flag for cell protection (cell locked and formula hidden)');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [b]),
'Text orientation and flags for used attribute groups');
end else
begin // sfExcel8
if Row = FCurrRow then begin
FDetails.Add('Text rotation angle:'#13);
if b = 0 then
FDetails.Add('not rotated')
else if b <= 90 then
FDetails.Add(Format('%d degrees counter-clockwise', [b]))
else if b <= 180 then
FDetails.Add(Format('%d degrees clockwize', [b-90]))
else if b = 255 then
FDetails.Add('not rotated, letters stacked top-to-bottom');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), 'Text rotation angle');
end;
if FFormat = sfExcel8 then begin
numBytes := 1;
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Indentation, shrink to cell size, and text direction:'#13);
FDetails.Add(Format('Bits 0-3: Indent level = %d', [b and $0F]));
if b and $10 = 0
then FDetails.Add('Bit $10 = 0: Don''t shrink content to fit into cell')
else FDetails.Add('Bit $10 = 1: Shrink content to fit into cell');
if b and $20 = 0
then FDetails.Add('Bit $20 = 0: Merge Cell option is OFF')
else FDetails.Add('Bit $20 = 1: Merge Cell option is ON');
case (b and $C0) shr 6 of
0: FDetails.Add('Bits 6-7 = 0: Text direction according to context');
1: FDetails.Add('Bits 6-7 = 1: Text direction left-to-right');
2: FDetails.Add('Bits 6-7 = 2: Text direction right-to-left');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]),
'Indentation, shrink to cell size, and text direction');
numBytes := 1;
b := FBuffer[FBufferIndex];
if Row = FCurrRow then begin
FDetails.Add('Flags for used attribute groups:'#13);
if b and $04 = 0
then FDetails.Add('Bit $04 = 0: No flag for number format')
else FDetails.Add('Bit $04 = 1: Flag for number format');
if b and $08 = 0
then FDetails.Add('Bit $08 = 0: No flag for font')
else FDetails.Add('Bit $08 = 2: Flag for font');
if b and $10 = 0
then FDetails.Add('Bit $10 = 0: No flag for hor/vert alignment, text wrap, indentation, orientation, rotation, and text direction')
else FDetails.Add('Bit $10 = 1: Flag for hor/vert alignment, text wrap, indentation, orientation, rotation, and text direction');
if b and $20 = 0
then FDetails.Add('Bit $20 = 0: No flag for border lines')
else FDetails.Add('Bit $20 = 1: Flag for border lines');
if b and $40 = 0
then FDetails.Add('Bit $40 = 0: No flag for background area style')
else FDetails.Add('Bit $40 = 1: Flag for background area style');
if b and $80 = 0
then FDetails.Add('Bit $80 = 0: No flag for cell protection (cell locked and formula hidden)')
else FDetails.Add('Bit $80 = 1: Flag for cell protection (cell locked and formula hidden)');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [b]),
'Flags for used attribute groups');
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
dw := DWordLEToN(dw);
if Row = FCurrRow then begin
FDetails.Add('Cell border lines and background area:'#13);
case dw and $0000000F of
$0000: FDetails.Add('Bits 0-3 = 0: Left border = No line');
$0001: FDetails.Add('Bits 0-3 = 1: Left border = thin solid line');
$0002: FDetails.Add('Bits 0-3 = 2: Left border = medium solid line');
$0003: FDetails.Add('Bits 0-3 = 3: Left border = dashed line');
$0004: FDetails.Add('Bits 0-3 = 4: Left border = dotted line');
$0005: FDetails.Add('Bits 0-3 = 5: Left border = thick solid line');
$0006: FDetails.Add('Bits 0-3 = 6: Left border = double solid line');
$0007: FDetails.Add('Bits 0-3 = 7: Left border = hair line');
$0008: FDetails.Add('Bits 0-3 = 8: Left border = medium dashed');
$0009: FDetails.Add('Bits 0-3 = 9: Left border = thin dash-dotted');
$000A: FDetails.Add('Bits 0-3 = 10: Left border = medium dash-dotted');
$000B: FDetails.Add('Bits 0-3 = 11: Left border = thin dash-dot-dotted');
$000C: FDetails.Add('Bits 0-3 = 12: Left border = medium dash-dot-dotted');
$000D: FDetails.Add('Bits 0-3 = 13: Left border = slanted medium dash-dotted');
end;
case dw and $000000F0 of
$0000: FDetails.Add('Bits 4-7 = 0: Right border = No line');
$0010: FDetails.Add('Bits 4-7 = 1: Right border = thin solid line');
$0020: FDetails.Add('Bits 4-7 = 2: Right border = medium solid line');
$0030: FDetails.Add('Bits 4-7 = 3: Right border = dashed line');
$0040: FDetails.Add('Bits 4-7 = 4: Right border = dotted line');
$0050: FDetails.Add('Bits 4-7 = 5: Right border = thick solid line');
$0060: FDetails.Add('Bits 4-7 = 6: Right border = double solid line');
$0070: FDetails.Add('Bits 4-7 = 7: Right border = hair line');
$0080: FDetails.Add('Bits 4-7 = 8: Right border = medium dashed');
$0090: FDetails.Add('Bits 4-7 = 9: Right border = thin dash-dotted');
$00A0: FDetails.Add('Bits 4-7 = 10: Right border = medium dash-dotted');
$00B0: FDetails.Add('Bits 4-7 = 11: Right border = thin dash-dot-dotted');
$00C0: FDetails.Add('Bits 4-7 = 12: Right border = medium dash-dot-dotted');
$00D0: FDetails.Add('Bits 4-7 = 13: Right border = slanted medium dash-dotted');
end;
case dw and $00000F00 of
$0000: FDetails.Add('Bits 8-11 = 0: Top border = No line');
$0100: FDetails.Add('Bits 8-11 = 1: Top border = thin solid line');
$0200: FDetails.Add('Bits 8-11 = 2: Top border = medium solid line');
$0300: FDetails.Add('Bits 8-11 = 3: Top border = dashed line');
$0400: FDetails.Add('Bits 8-11 = 4: Top border = dotted line');
$0500: FDetails.Add('Bits 8-11 = 5: Top border = thick solid line');
$0600: FDetails.Add('Bits 8-11 = 6: Top border = double solid line');
$0700: FDetails.Add('Bits 8-11 = 7: Top border = hair line');
$0800: FDetails.Add('Bits 8-11 = 8: Top border = medium dashed');
$0900: FDetails.Add('Bits 8-11 = 9: Top border = thin dash-dotted');
$0A00: FDetails.Add('Bits 8-11 = 10: Top border = medium dash-dotted');
$0B00: FDetails.Add('Bits 8-11 = 11: Top border = thin dash-dot-dotted');
$0C00: FDetails.Add('Bits 8-11 = 12: Top border = medium dash-dot-dotted');
$0D00: FDetails.Add('Bits 8-11 = 13: Top border = slanted medium dash-dotted');
end;
case dw and $0000F000 of
$0000: FDetails.Add('Bits 12-15 = 0: Bottom border = No line');
$1000: FDetails.Add('Bits 12-15 = 1: Bottom border = thin solid line');
$2000: FDetails.Add('Bits 12-15 = 2: Bottom border = medium solid line');
$3000: FDetails.Add('Bits 12-15 = 3: Bottom border = dashed line');
$4000: FDetails.Add('Bits 12-15 = 4: Bottom border = dotted line');
$5000: FDetails.Add('Bits 12-15 = 5: Bottom border = thick solid line');
$6000: FDetails.Add('Bits 12-15 = 6: Bottom border = double solid line');
$7000: FDetails.Add('Bits 12-15 = 7: Bottom border = hair line');
$8000: FDetails.Add('Bits 12-15 = 8: Bottom border = medium dashed');
$9000: FDetails.Add('Bits 12-15 = 9: Bottom border = thin dash-dotted');
$A000: FDetails.Add('Bits 12-15 = 10: Bottom border = medium dash-dotted');
$B000: FDetails.Add('Bits 12-15 = 11: Bottom border = thin dash-dot-dotted');
$C000: FDetails.Add('Bits 12-15 = 12: Bottom border = medium dash-dot-dotted');
$D000: FDetails.Add('Bits 12-15 = 13: Bottom border = slanted medium dash-dotted');
end;
FDetails.Add(Format('Bits 16-22 = %d: Color index for left line color', [(dw and $007F0000) shr 16]));
FDetails.Add(Format('Bits 23-29 = %d: Color index for right line color', [(dw and $3F800000) shr 23]));
if dw and $40000000 = 0
then FDetails.Add('Bit 30 = 0: No diagonal line from top left to right bottom')
else FDetails.Add('Bit 30 = 1: Diagonal line from top left to right bottom');
if dw and $80000000 = 0
then FDetails.Add('Bit 31 = 0: No diagonal line from bottom left to right top')
else FDetails.Add('Bit 31 = 1: Diagonal line from bottom left to right top');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]),
'Cell border lines and background area');
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
if Row = FCurrRow then begin
FDetails.Add('Cell border lines and background area (cont''d):'#13);
FDetails.Add(Format('Bits 0-6 = %d: Color index for top line color', [(dw and $0000007F)]));
FDetails.Add(Format('Bits 7-13 = %d: Color index for bottom line color', [(dw and $00003F80) shr 7]));
FDetails.Add(Format('Bits 14-20 = %d: Color index for diagonal line color', [(dw and $001FC000) shr 14]));
case dw and $01E00000 shr 17 of
$0: FDetails.Add('Bits 21-24 = 0: Diagonal line style = No line');
$1: FDetails.Add('Bits 21-24 = 1: Diagonal line style = thin solid line');
$2: FDetails.Add('Bits 21-24 = 2: Diagonal line style = medium solid line');
$3: FDetails.Add('Bits 21-24 = 3: Diagonal line style = dashed line');
$4: FDetails.Add('Bits 21-24 = 4: Diagonal line style = dotted line');
$5: FDetails.Add('Bits 21-24 = 5: Diagonal line style = thick solid line');
$6: FDetails.Add('Bits 21-24 = 6: Diagonal line style = double solid line');
$7: FDetails.Add('Bits 21-24 = 7: Diagonal line style = hair line');
$8: FDetails.Add('Bits 21-24 = 8: Diagonal line style = medium dashed');
$9: FDetails.Add('Bits 21-24 = 9: Diagonal line style = thin dash-dotted');
$A: FDetails.Add('Bits 21-24 = 10: Diagonal line style = medium dash-dotted');
$B: FDetails.Add('Bits 21-24 = 11: Diagonal line style = thin dash-dot-dotted');
$C: FDetails.Add('Bits 21-24 = 12: Diagonal line style = medium dash-dot-dotted');
$D: FDetails.Add('Bits 21-24 = 13: Diagonal line style = slanted medium dash-dotted');
end;
case (dw and $FC000000) shr 26 of
$00: FDetails.Add('Bits 26-31 = 0: Fill pattern = No fill');
$01: FDetails.Add('Bits 26-31 = 1: Fill pattern = solid fill');
$02: FDetails.Add('Bits 26-31 = 2: Fill pattern = medium fill');
$03: FDetails.Add('Bits 26-31 = 3: Fill pattern = dense fill');
$04: FDetails.Add('Bits 26-31 = 4: Fill pattern = sparse fill');
$05: FDetails.Add('Bits 26-31 = 5: Fill pattern = horizontal fill');
$06: FDetails.Add('Bits 26-31 = 6: Fill pattern = vertical fill');
$07: FDetails.Add('Bits 26-31 = 7: Fill pattern = backslash fill');
$08: FDetails.Add('Bits 26-31 = 8: Fill pattern = slash fill');
$09: FDetails.Add('Bits 26-31 = 9: Fill pattern = coarse medium fill');
$0A: FDetails.Add('Bits 26-31 = 10: Fill pattern = coarse medium horiz fill');
$0B: FDetails.Add('Bits 26-31 = 11: Fill pattern = sparse horizontal fill');
$0C: FDetails.Add('Bits 26-31 = 12: Fill pattern = sparse vertical fill');
$0D: FDetails.Add('Bits 26-31 = 13: Fill pattern = sparse backslash fill');
$0E: FDetails.Add('Bits 26-31 = 14: Fill pattern = sparse slash fill');
$0F: FDetails.Add('Bits 26-31 = 15: Fill pattern = cross fill');
$10: FDetails.Add('Bits 26-31 = 16: Fill pattern = dense backslash fill');
$11: FDetails.Add('Bits 26-31 = 17: Fill pattern = very sparse fill');
$12: FDetails.Add('Bits 26-31 = 18: Fill pattern = extremely sparse fill');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]),
'Cell border lines and background area (cont''d)');
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Cell border lines and background area (cont''d):'#13);
FDetails.Add(Format('Bits 0-6 = %d: Color index for pattern color', [(w and $007F)]));
FDetails.Add(Format('Bits 7-13 = %d: Color index for pattern background color', [(w and $3F80) shr 7]));
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]),
'Cell border lines and background area (cont''d)');
end;
if FFormat = sfExcel5 then begin
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numBytes);
dw := DWordLEToN(dw);
if Row = FCurrRow then begin
FDetails.Add('Cell border lines and background area:'#13);
FDetails.Add(Format('Bits 0-6 = %d: Color index for pattern color', [(dw and $007F)]));
FDetails.Add(Format('Bits 7-13 = %d: Color index for pattern background color', [(dw and $3F80) shr 7]));
case (dw and $003F0000) shr 16 of
$00: FDetails.Add('Bits 16-21 = 0: Fill pattern = No fill');
$01: FDetails.Add('Bits 16-21 = 1: Fill pattern = solid fill');
$02: FDetails.Add('Bits 16-21 = 2: Fill pattern = medium fill');
$03: FDetails.Add('Bits 16-21 = 3: Fill pattern = dense fill');
$04: FDetails.Add('Bits 16-21 = 4: Fill pattern = sparse fill');
$05: FDetails.Add('Bits 16-21 = 5: Fill pattern = horizontal fill');
$06: FDetails.Add('Bits 16-21 = 6: Fill pattern = vertical fill');
$07: FDetails.Add('Bits 16-21 = 7: Fill pattern = backslash fill');
$08: FDetails.Add('Bits 16-21 = 8: Fill pattern = slash fill');
$09: FDetails.Add('Bits 16-21 = 9: Fill pattern = coarse medium fill');
$0A: FDetails.Add('Bits 16-21 = 10: Fill pattern = coarse medium horiz fill');
$0B: FDetails.Add('Bits 16-21 = 11: Fill pattern = sparse horizontal fill');
$0C: FDetails.Add('Bits 16-21 = 12: Fill pattern = sparse vertical fill');
$0D: FDetails.Add('Bits 16-21 = 13: Fill pattern = sparse backslash fill');
$0E: FDetails.Add('Bits 16-21 = 14: Fill pattern = sparse slash fill');
$0F: FDetails.Add('Bits 16-21 = 15: Fill pattern = cross fill');
$10: FDetails.Add('Bits 16-21 = 16: Fill pattern = dense backslash fill');
$11: FDetails.Add('Bits 16-21 = 17: Fill pattern = very sparse fill');
$12: FDetails.Add('Bits 16-21 = 18: Fill pattern = extremely sparse fill');
end;
case dw and $01C00000 shr 22 of
$0: FDetails.Add('Bits 22-24 = 0: Bottom line style = No line');
$1: FDetails.Add('Bits 22-24 = 1: Bottom line style = thin solid line');
$2: FDetails.Add('Bits 22-24 = 2: Bottom line style = medium solid line');
$3: FDetails.Add('Bits 22-24 = 3: Bottom line style = dashed line');
$4: FDetails.Add('Bits 22-24 = 4: Bottom line style = dotted line');
$5: FDetails.Add('Bits 22-24 = 5: Bottom line style = thick solid line');
$6: FDetails.Add('Bits 22-24 = 6: Bottom line style = double solid line');
$7: FDetails.Add('Bits 22-24 = 7: Bottom line style = hair line');
end;
FDetails.Add(Format('Bits 25-31 = %d: Color index for bottom line color', [(dw and $FE000000) shr 25]));
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]),
'Cell border lines & background area');
numBytes := 4;
Move(FBuffer[FBufferIndex], dw, numBytes);
dw := DWOrdLEToN(dw);
if Row = FCurrRow then begin
FDetails.Add('Cell border lines (cont''d):'#13);
case dw and $00000007 of
$00: FDetails.Add('Bits 0-2 = 0: Top border = No line');
$01: FDetails.Add('Bits 0-2 = 1: Top border = thin solid line');
$02: FDetails.Add('Bits 0-2 = 2: Top border = medium solid line');
$03: FDetails.Add('Bits 0-2 = 3: Top border = dashed line');
$04: FDetails.Add('Bits 0-2 = 4: Top border = dotted line');
$05: FDetails.Add('Bits 0-2 = 5: Top border = thick solid line');
$06: FDetails.Add('Bits 0-2 = 6: Top border = double solid line');
$07: FDetails.Add('Bits 0-2 = 7: Top border = hair line');
end;
case (dw and $00000038) shr 3 of
$0000: FDetails.Add('Bits 3-5 = 0: Left border = No line');
$0001: FDetails.Add('Bits 3-5 = 1: Left border = thin solid line');
$0002: FDetails.Add('Bits 3-5 = 2: Left border = medium solid line');
$0003: FDetails.Add('Bits 3-5 = 3: Left border = dashed line');
$0004: FDetails.Add('Bits 3-5 = 4: Left border = dotted line');
$0005: FDetails.Add('Bits 3-5 = 5: Left border = thick solid line');
$0006: FDetails.Add('Bits 3-5 = 6: Left border = double solid line');
$0007: FDetails.Add('Bits 3-5 = 7: Left border = hair line');
end;
case (dw and $000001C0) shr 6 of
$0000: FDetails.Add('Bits 6-8 = 0: Right border = No line');
$0010: FDetails.Add('Bits 6-8 = 1: Right border = thin solid line');
$0020: FDetails.Add('Bits 6-8 = 2: Right border = medium solid line');
$0030: FDetails.Add('Bits 6-8 = 3: Right border = dashed line');
$0040: FDetails.Add('Bits 6-8 = 4: Right border = dotted line');
$0050: FDetails.Add('Bits 6-8 = 5: Right border = thick solid line');
$0060: FDetails.Add('Bits 6-8 = 6: Right border = double solid line');
$0070: FDetails.Add('Bits 6-8 = 7: Right border = hair line');
end;
FDetails.Add(Format('Bits 9-15 = %d: Color index for top line color', [(dw and $0000FE00) shr 7]));
FDetails.Add(Format('Bits 16-22 = %d: Color index for left line color', [(dw and $007F0000) shr 16]));
FDetails.Add(Format('Bits 23-29 = %d: Color index for right line color', [(dw and $3F800000) shr 23]));
end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.8x', [dw]),
'Cell border lines (cont''d)');
end;
end;
end;
procedure TBIFFGrid.ShowXFCRC;
var
numBytes: Integer;
w: Word;
dw: DWord;
begin
RowCount := FixedRows + 7;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [wordLEToN(w)]),
'Future record type');
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Attributes:'#13);
if w and $0001 = 0
then FDetails.Add(' Bit 0 = 0: The containing record does not specify a range of cells.')
else FDetails.Add('x Bit 0 = 1: The containing record specifies a range of cells.');
FDetails.Add('Bit 1: specifies wether to alert the user of possible problems '+
'when saving the file whithout having reckognized this record.');
FDetails.Add('Bits 2-15: reserved (MUST be zero, MUST be ignored)');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]),
'Attributes');
numbytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), 'Reserved');
Move(FBuffer[FBufferIndex], dw, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), 'Reserved');
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), 'Reserved');
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
'Count of XF records');
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w,w]),
'Checksum of XF records');
end;
procedure TBIFFGrid.ShowXFEXT;
var
numBytes: Integer;
w: Word;
dw: DWord;
i, n: Integer;
et: Word;
es: Word;
ct: Word;
buffidx: Cardinal;
s: String;
begin
BeginUpdate;
RowCount := FixedRows + 100;
numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [wordLEToN(w)]),
'Future record type');
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Attributes:'#13);
if w and $0001 = 0
then FDetails.Add(' Bit 0 = 0: The containing record does not specify a range of cells.')
else FDetails.Add('x Bit 0 = 1: The containing record specifies a range of cells.');
FDetails.Add('Bit 1: specifies wether to alert the user of possible problems '+
'when saving the file whithout having reckognized this record.');
FDetails.Add('Bits 2-15: reserved (MUST be zero, MUST be ignored)');
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]),
'Attributes');
numbytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), 'Reserved');
Move(FBuffer[FBufferIndex], dw, numbytes);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), 'Reserved');
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), 'Reserved');
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w,w]),
'XF index');
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), 'Reserved');
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
'Number of extension properties');
n := w;
for i:=1 to n do begin
buffidx := FBufferIndex;
numbytes := 2;
Move(FBuffer[FBufferIndex], et, numbytes);
et := WordLEToN(et);
if Row = FCurrRow then begin
FDetails.Add('Type:'#13);
case et of
$04: FDetails.Add('Full color extension that specifies the cell interior foreground color.');
$05: FDetails.Add('Full color extension that specifies the cell interior background color.');
$06: FDetails.Add('Gradient extension that specifies a cell interior gradient fill.');
$07: FDetails.Add('Full color extension that specifies the top cell border color.');
$08: FDetails.Add('Full color extension that specifies the bottom cell border color.');
$09: FDetails.Add('Full color extension that specifies the left cell border color.');
$0A: FDetails.Add('Full color extension that specifies the right cell border color.');
$0B: FDetails.Add('Full color extension that specifies the diagonal cell border color.');
$0D: FDetails.Add('Full color extension that specifies the cell text color.');
$0E: FDetails.Add('2-byte unsigned integer that specifies a font scheme.');
$0F: FDetails.Add('2-byte unsigned integer that specifies the text indentation level (MUST be <= 250).');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [et, et]),
Format('Extension property #%d: Type', [i]));
Move(FBuffer[FBufferIndex], es, numbytes);
es := WordLEToN(es);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(es),
Format('Extension property #%d: Data size', [i]));
case et of
$04, $05, $07..$0D: // FullColorExt
begin
numbytes := 2;
Move(FBuffer[FBufferIndex], ct, numbytes);
ct := WordLEToN(ct);
if Row = FCurrRow then begin
FDetails.Add('Full color extension - Color type:'#13);
case ct of
0: FDetails.Add('0 - Automatic color');
1: FDetails.Add('1 - Indexed color');
2: FDetails.Add('2 - RGB color');
3: FDetails.Add('3 - Theme color');
4: FDetails.Add('4 - Color not set');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(ct),
Format('Extension property #%d (Full color extension): Color type', [i]));
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(integer(w)),
Format('Extension property #%d (Full color extension): Color tint', [i]));
numbytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
dw := DWordLEToN(dw);
case ct of
0: s := '(dummy - MUST be 0)';
1: s := '(index)';
2: s := '(RGB value)';
3: s := '(theme)';
else s := '';
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.8x)', [QWord(dw), QWord(dw)]),
Format('Extension property #%d (Full color extension): value %s', [i, s]));
numbytes := 4;
Move(FBuffer[FBufferIndex], dw, numbytes);
dw := DWordLEToN(dw);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.8x)', [QWord(dw), QWord(dw)]),
Format('Extension property #%d (Full color extension): Reserved', [i]));
Move(FBuffer[FBufferIndex], dw, numbytes);
dw := DWordLEToN(dw);
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.8x)', [QWord(dw), QWord(dw)]),
Format('Extension property #%d (Full color extension): Reserved', [i]));
end;
$06: // Gradient
begin
ShowInRow(FCurrRow, FBufferIndex, es, '(var)',
Format('Extension property #%d (Gradient): - not interpreted here -', [i]));
end;
$0E: // Font scheme
begin
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
if Row = FCurrRow then begin
FDetails.Add('Font scheme:'#13);
case w of
0: FDetails.Add('0 - No font scheme');
1: FDetails.Add('1 - Major scheme');
2: FDetails.Add('2 - Minor scheme');
3: FDetails.Add('3 - Ninched scheme');
end;
end;
ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w,w]),
Format('Extension property #%d Font scheme', [i]));
end;
$0F: // Text indentation level
begin
numbytes := 2;
Move(FBuffer[FBufferIndex], w, numbytes);
w := WordLEToN(w);
ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
Format('Extension property #%d Text indentation level', [i]));
end;
end;
FBufferIndex := buffidx + es;
end;
RowCount := FCurrRow;
EndUpdate(true);
end;
end.